<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Programming | Attack on Life</title><link>https://en.1991421.cn/category/programming/</link><atom:link href="https://en.1991421.cn/category/programming/index.xml" rel="self" type="application/rss+xml"/><description>Programming</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-US</language><lastBuildDate>Sat, 16 May 2026 19:34:00 +0800</lastBuildDate><image><url>https://en.1991421.cn/media/sharing.png</url><title>Programming</title><link>https://en.1991421.cn/category/programming/</link></image><item><title>Why img onerror Doesn't Trigger on HTTP 404: A Common Pitfall</title><link>https://en.1991421.cn/2026/05/16/img-onerror-not-triggered-on-404/</link><pubDate>Sat, 16 May 2026 19:34:00 +0800</pubDate><guid>https://en.1991421.cn/2026/05/16/img-onerror-not-triggered-on-404/</guid><description>&lt;blockquote>
&lt;p>The &lt;code>img&lt;/code> element has an &lt;code>onerror&lt;/code> event, which in theory fires when an image fails to load. Recently, however, I ran into a case where it didn&amp;rsquo;t fire. After digging in, I found a subtle detail worth knowing.&lt;/p>
&lt;/blockquote>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/05/2026-05-16-194106.jpeg"
alt="https://static.1991421.cn/2026/05/2026-05-16-194106.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># Does NOT trigger onerror
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;img src=&amp;#34;https://i.postimg.cc/76vzQ3fQ/C01.jpg&amp;#34; onerror=&amp;#34;alert(111)&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># DOES trigger onerror
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;img src=&amp;#34;C01.jpg&amp;#34; onerror=&amp;#34;alert(111)&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, the image request returns a 404 status code, but the response body is still valid image content, so &lt;code>onerror&lt;/code> does not fire. The conclusion: &lt;code>onerror&lt;/code> doesn&amp;rsquo;t care about the HTTP status code — it only cares whether the response is a properly-formed image.&lt;/p>
&lt;h2 id="what-mdn-says-about-onerror">
&lt;a class="heading-anchor-link" href="#what-mdn-says-about-onerror">What MDN Says About onerror&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-mdn-says-about-onerror"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If an error occurs while loading or rendering an image, and an onerror event handler has been set for the error event, that event handler will get called. This can happen in several situations, including:&lt;/p>
&lt;ul>
&lt;li>The src or srcset attributes are empty (&amp;quot;&amp;quot;) or null.&lt;/li>
&lt;li>The src URL is the same as the URL of the page the user is currently on.&lt;/li>
&lt;li>The image is corrupted in some way that prevents it from being loaded.&lt;/li>
&lt;li>The image&amp;rsquo;s metadata is corrupted in such a way that it&amp;rsquo;s impossible to retrieve its dimensions, and no dimensions were specified in the &lt;img> element&amp;rsquo;s attributes.&lt;/li>
&lt;li>The image is in a format not supported by the user agent.&lt;/li>
&lt;/ul>
&lt;p>So MDN confirms that &lt;code>onerror&lt;/code> is triggered based on whether the image can be properly loaded — not based on the HTTP status code.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>onerror&lt;/code> only works as a fallback when the image itself cannot be loaded. If the server returns a 404 status code but the response body is still a valid image, &lt;code>onerror&lt;/code> won&amp;rsquo;t fire. Keep this in mind when relying on &lt;code>onerror&lt;/code>. If you really need to make decisions based on the HTTP status code, consider an alternative approach — for example, using &lt;code>fetch&lt;/code> to request the image first, checking the status code, and only then setting the &lt;code>src&lt;/code> attribute on the &lt;code>img&lt;/code> element.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Trezor Hardware Wallet: Firmware, Emulator, and Custom Firmware Notes</title><link>https://en.1991421.cn/2026/04/28/trezor-hardware-wallet/</link><pubDate>Tue, 28 Apr 2026 16:49:49 +0800</pubDate><guid>https://en.1991421.cn/2026/04/28/trezor-hardware-wallet/</guid><description>&lt;blockquote>
&lt;p>imKey does not support flashing custom firmware, so our team looked into another hardware wallet brand: Trezor. I am noting down some resources here for future reference.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/04/2026-04-28-231814.jpeg"
alt="https://static.1991421.cn/2026/04/2026-04-28-231814.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="firmware">
&lt;a class="heading-anchor-link" href="#firmware">Firmware&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="firmware"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/trezor/trezor-firmware" target="_blank" rel="noopener">https://github.com/trezor/trezor-firmware&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>It looks like all models share one unified firmware repository.&lt;/p>
&lt;h2 id="app-support">
&lt;a class="heading-anchor-link" href="#app-support">App Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="app-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://github.com/trezor/trezor-firmware/tree/main/core/src/apps" target="_blank" rel="noopener">https://github.com/trezor/trezor-firmware/tree/main/core/src/apps&lt;/a>&lt;/p>
&lt;h2 id="emulator">
&lt;a class="heading-anchor-link" href="#emulator">Emulator&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="emulator"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.trezor.io/trezor-firmware/core/build/emulator.html" target="_blank" rel="noopener">https://docs.trezor.io/trezor-firmware/core/build/emulator.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="custom-firmware">
&lt;a class="heading-anchor-link" href="#custom-firmware">Custom Firmware&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-firmware"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://trezor.io/trezor-suite?srsltid=AfmBOortBR02GKnAknyFOeS8YWKwWmQuYZeSHtu-ix4v0_7UJaBYd-5w" target="_blank" rel="noopener">https://trezor.io/trezor-suite?srsltid=AfmBOortBR02GKnAknyFOeS8YWKwWmQuYZeSHtu-ix4v0_7UJaBYd-5w&lt;/a>&lt;/li>
&lt;/ol>
&lt;h3 id="trezor-3">
&lt;a class="heading-anchor-link" href="#trezor-3">trezor 3&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="trezor-3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;a href="https://trezor.io/support/troubleshooting/device-issues/install-custom-firmware-on-trezor-safe-3" target="_blank" rel="noopener">https://trezor.io/support/troubleshooting/device-issues/install-custom-firmware-on-trezor-safe-3&lt;/a>&lt;/li>
&lt;/ol>
&lt;h3 id="trezor-5">
&lt;a class="heading-anchor-link" href="#trezor-5">trezor 5&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="trezor-5"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;a href="https://trezor.io/support/troubleshooting/device-issues/install-custom-firmware-on-trezor-safe-5" target="_blank" rel="noopener">https://trezor.io/support/troubleshooting/device-issues/install-custom-firmware-on-trezor-safe-5&lt;/a>&lt;/li>
&lt;/ol>
&lt;h3 id="trezor-7">
&lt;a class="heading-anchor-link" href="#trezor-7">trezor 7&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="trezor-7"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;a href="https://trezor.io/support/troubleshooting/device-issues/install-custom-firmware-on-trezor-safe-7?srsltid=AfmBOopwRAgh3ldC25awGsoDHbApnG7UgThYw8xWUDtUtBZE8_MHj3QC" target="_blank" rel="noopener">https://trezor.io/support/troubleshooting/device-issues/install-custom-firmware-on-trezor-safe-7?srsltid=AfmBOopwRAgh3ldC25awGsoDHbApnG7UgThYw8xWUDtUtBZE8_MHj3QC&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="twitter-follower-count">
&lt;a class="heading-anchor-link" href="#twitter-follower-count">Twitter Follower Count&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="twitter-follower-count"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Trezor has about 230k followers.&lt;/li>
&lt;li>Ledger has about 670k followers.
Based on the numbers, Trezor has roughly one third of Ledger&amp;rsquo;s follower count, which may suggest its community influence is lower than Ledger&amp;rsquo;s.&lt;/li>
&lt;/ol>
&lt;h2 id="web-to-wallet-communication">
&lt;a class="heading-anchor-link" href="#web-to-wallet-communication">Web-to-Wallet Communication&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web-to-wallet-communication"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>@trezor/connect-web&lt;/p>
&lt;h2 id="firmware-development--build">
&lt;a class="heading-anchor-link" href="#firmware-development--build">Firmware Development / Build&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="firmware-development--build"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">clone&lt;/span> &lt;span class="n">git&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">github&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">trezor&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">trezor&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">firmware&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">git&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fetch all submodule resources.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">git&lt;/span> &lt;span class="n">submodule&lt;/span> &lt;span class="n">update&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">init&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">recursive&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">force&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">progress&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install nix. It supports Linux/macOS, and you need to restart the shell after installation.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">curl&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">L&lt;/span> &lt;span class="n">https&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="n">nixos&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nix&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">install&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">sh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">nix&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">shell&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">arg&lt;/span> &lt;span class="n">devTools&lt;/span> &lt;span class="bp">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Environment.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">uv&lt;/span> &lt;span class="n">sync&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">source&lt;/span> &lt;span class="o">.&lt;/span>&lt;span class="n">venv&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">activate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Test whether the environment is set up correctly.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">python&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="s2">&amp;#34;import click; print(&amp;#39;click ok&amp;#39;)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Enter the core directory and install dependencies.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">cd&lt;/span> &lt;span class="n">core&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fetch third-party dependencies.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">make&lt;/span> &lt;span class="n">vendor&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Note: if the code has not changed but you build the firmware repeatedly, it will error because the file already exists.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># A normal build will print something like: cp build/firmware/firmware.bin build/firmware/firmware-T3T1-2.11.2-6daa3bd.bin (trezor-firmware)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">make&lt;/span> &lt;span class="n">build_firmware&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># For example, if the device model is T3B1, specify the environment variable.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">TREZOR_MODEL&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">T3B1&lt;/span> &lt;span class="n">make&lt;/span> &lt;span class="n">build_firmware&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># TREZOR_MODEL=T3B1 make upload&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Device list, used to confirm the device is connected and recognized correctly.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">trezorctl&lt;/span> &lt;span class="n">list&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="firmware-file">
&lt;a class="heading-anchor-link" href="#firmware-file">Firmware File&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="firmware-file"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After building, the file is at &lt;code>core/build/firmware/firmware-T3B1-2.11.3-3a8d901.bin&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/05/2026-05-13-105333.jpeg"
alt="https://static.1991421.cn/2026/05/2026-05-13-105333.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: if your local git tree has uncommitted changes, the built firmware will include a dirty marker.&lt;/p>
&lt;h2 id="check-the-devices-real-model">
&lt;a class="heading-anchor-link" href="#check-the-devices-real-model">Check the Device&amp;rsquo;s Real Model&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="check-the-devices-real-model"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">trezorctl get-features | grep -E &amp;#34;model|internal_model|vendor|major_version&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="flashing-firmware">
&lt;a class="heading-anchor-link" href="#flashing-firmware">Flashing Firmware&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="flashing-firmware"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="unlocking-add-this-if-the-prompt-does-not-pass">
&lt;a class="heading-anchor-link" href="#unlocking-add-this-if-the-prompt-does-not-pass">Unlocking &lt;code>add this if the prompt does not pass&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="unlocking-add-this-if-the-prompt-does-not-pass"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">trezorctl&lt;/span> &lt;span class="n">device&lt;/span> &lt;span class="n">unlock&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bootloader&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Unlock it according to the on-device prompts.&lt;/p>
&lt;p>Note: &lt;code>trezorctl&lt;/code> is the official CLI tool written in Python. I recommend following the firmware development flow to install the environment.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ git clone git@github.com:trezor/trezor-firmware.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fetch all submodule resources.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git submodule update --init --recursive --force --progress
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install nix. It supports Linux/macOS, and you need to restart the shell after installation.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ curl -L https://nixos.org/nix/install &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ nix-shell --arg devTools &lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Environment.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ uv sync
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ &lt;span class="nb">source&lt;/span> .venv/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># At this point, trezorctl commands should work. For example, trezorctl list can show the device list.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="enter-bootloader-mode">
&lt;a class="heading-anchor-link" href="#enter-bootloader-mode">Enter Bootloader Mode&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enter-bootloader-mode"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Hold down both hardware wallet buttons and connect it to the computer to enter bootloader mode.&lt;/li>
&lt;li>Tap &lt;code>Install FW&lt;/code> on the hardware wallet. It will show &lt;code>waiting for host&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h3 id="install-firmware-from-the-command-line-method-1">
&lt;a class="heading-anchor-link" href="#install-firmware-from-the-command-line-method-1">Install Firmware from the Command Line &lt;code>method 1&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-firmware-from-the-command-line-method-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This will trigger firmware flashing on the physical device. You can also use Trezor Suite to flash firmware.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">make&lt;/span> &lt;span class="n">upload&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="install-firmware-with-trezor-suite-method-2">
&lt;a class="heading-anchor-link" href="#install-firmware-with-trezor-suite-method-2">Install Firmware with Trezor Suite &lt;code>method 2&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-firmware-with-trezor-suite-method-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The prerequisite is entering bootloader mode. After entering it, the hardware wallet will prompt for installation; tap install, then choose the connected device from the top-left corner on the PC side.&lt;/p>
&lt;p>Download Trezor Suite from the &lt;a href="https://trezor.io/trezor-suite" target="_blank" rel="noopener">official site&lt;/a>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/05/2026-05-13-115155.jpeg"
alt="https://static.1991421.cn/2026/05/2026-05-13-115155.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/05/2026-05-13-115316.jpeg"
alt="https://static.1991421.cn/2026/05/2026-05-13-115316.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/05/2026-05-09-164639.jpeg"
alt="https://static.1991421.cn/2026/05/2026-05-09-164639.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: firmware size is only about &lt;code>1-2MB&lt;/code> in my test, so installation is fast. It needs confirmation on the hardware wallet side.&lt;/p>
&lt;p>Note: after flashing locally custom-signed firmware, the wallet will show a warning on startup. The warning disappears only after flashing official signed firmware again.&lt;/p>
&lt;h2 id="web3js-and-trezor-interaction">
&lt;a class="heading-anchor-link" href="#web3js-and-trezor-interaction">web3.js and Trezor Interaction&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web3js-and-trezor-interaction"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>It uses the PB message protocol.&lt;/li>
&lt;li>Communication goes through WebUSB or the bridge protocol.&lt;/li>
&lt;/ul>
&lt;h2 id="related-docs">
&lt;a class="heading-anchor-link" href="#related-docs">Related Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.trezor.io/trezor-firmware/core/build/embedded.html" target="_blank" rel="noopener">https://docs.trezor.io/trezor-firmware/core/build/embedded.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>China Website ICP Filing: Domain, Server, and Practical Notes</title><link>https://en.1991421.cn/2026/04/14/about-china-internal-website-beian-issues/</link><pubDate>Tue, 14 Apr 2026 12:16:31 +0800</pubDate><guid>https://en.1991421.cn/2026/04/14/about-china-internal-website-beian-issues/</guid><description>&lt;h2 id="does-icp-filing-follow-the-domain-or-the-server">
&lt;a class="heading-anchor-link" href="#does-icp-filing-follow-the-domain-or-the-server">Does ICP Filing Follow the Domain or the Server?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="does-icp-filing-follow-the-domain-or-the-server"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example: after a domain is filed through Alibaba Cloud, it can point to a Tencent Cloud server, but you need to complete an access filing first. According to the relevant rules, ICP filing follows the cloud server, not the domain. If your domain was filed with Alibaba Cloud but you want to point it to a Tencent Cloud server, you need to go through the access filing process.&lt;/p>
&lt;p>So the conclusion is: ICP filing follows the server, not the domain. No matter where your domain was originally filed, as long as the server is in mainland China, filing is required. Of course, if you move from one Alibaba Cloud server to another Alibaba Cloud server, you generally do not need to file again.&lt;/p>
&lt;h2 id="how-long-does-filing-take">
&lt;a class="heading-anchor-link" href="#how-long-does-filing-take">How Long Does Filing Take?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-long-does-filing-take"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Domestic cloud provider pages usually show an estimated time. In short, it is not very efficient. Just assume the timeline will not be optimistic and evaluate based on the longest expected duration.&lt;/p>
&lt;h2 id="filing-entity">
&lt;a class="heading-anchor-link" href="#filing-entity">Filing Entity&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="filing-entity"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes one company has multiple legal entities, so pay attention to the filing entity. I recently ran into an issue where the WeChat developer account entity did not match the filing entity of the domain I wanted to bind, which caused problems.&lt;/p>
&lt;h2 id="does-hosting-in-hong-kong-or-overseas-avoid-filing">
&lt;a class="heading-anchor-link" href="#does-hosting-in-hong-kong-or-overseas-avoid-filing">Does Hosting in Hong Kong or Overseas Avoid Filing?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="does-hosting-in-hong-kong-or-overseas-avoid-filing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Yes, but there are tradeoffs. For example, if you want to use Baidu Webmaster tools or earn ad revenue in China, it may not work, because domestic sites must have ICP filing.&lt;/p>
&lt;h2 id="is-the-filing-number-required-in-the-footer">
&lt;a class="heading-anchor-link" href="#is-the-filing-number-required-in-the-footer">Is the Filing Number Required in the Footer?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-the-filing-number-required-in-the-footer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Yes. According to the relevant rules, websites operated in mainland China must display the ICP filing number in the page footer. The filing number indicates that the website has completed the required filing process and meets the relevant requirements.&lt;/li>
&lt;li>For the homepage, or for a closed system, the login page must show the filing number. Other pages may not need it, but for an open system, I recommend placing it on every page because users may directly access pages other than the homepage.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>If your business targets mainland China, you cannot move forward without ICP filing. The process is unpleasant, but there is no real workaround.&lt;/li>
&lt;li>If you do not care about mainland China business, then you have much more freedom.&lt;/li>
&lt;/ol></description></item><item><title>How to Use Exa AI Search with Claude MCP</title><link>https://en.1991421.cn/2026/04/05/how-to-use-exa-ai-search-with-claude-mcp/</link><pubDate>Sun, 05 Apr 2026 20:57:48 +0800</pubDate><guid>https://en.1991421.cn/2026/04/05/how-to-use-exa-ai-search-with-claude-mcp/</guid><description>&lt;blockquote>
&lt;p>Most AI tools now have web search capabilities. For example, Claude Code can use online search, and when the AI performs a web lookup, you can clearly see it calling WebFetch. I heard that Exa is a good AI search engine, so I used it for a while and found it genuinely strong. Here is my experience.&lt;/p>
&lt;/blockquote>
&lt;h2 id="advantages-of-exa-search">
&lt;a class="heading-anchor-link" href="#advantages-of-exa-search">Advantages of Exa Search&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages-of-exa-search"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>My understanding is: 1. quality, 2. speed. AI web search is essentially direct search, but Exa appears to have its own index, so the quality is much better and the response is also much faster.&lt;/p>
&lt;h2 id="freepaid">
&lt;a class="heading-anchor-link" href="#freepaid">Free/Paid&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="freepaid"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Exa currently has a &lt;code>free credit allowance&lt;/code>, but it is still a paid product. Registration gives you &lt;code>10 USD&lt;/code> in credits, though this amount often changes, so use the official information as the source of truth. The free credits can last for a while, and after that you need to pay. Fortunately, it supports domestic Visa cards.&lt;/p>
&lt;p>I am still using free credits at the moment, since I have more than 60 dollars available. After I use them up, I may consider paying.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/2026-01-14-184225.jpeg"
alt="https://static.1991421.cn/2026/2026-01-14-184225.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="credit-promo-codes">
&lt;a class="heading-anchor-link" href="#credit-promo-codes">Credit Promo Codes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="credit-promo-codes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Exa occasionally runs campaigns that provide credit promo codes. If you have one, you can redeem it on &lt;a href="https://dashboard.exa.ai/billing" target="_blank" rel="noopener">this page&lt;/a>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/04/2026-04-05-210327.jpeg"
alt="https://static.1991421.cn/2026/04/2026-04-05-210327.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If you cannot find a promo code, try looking on social media or Xianyu.&lt;/p>
&lt;h2 id="install">
&lt;a class="heading-anchor-link" href="#install">Install&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Exa provides MCP, SDKs, and APIs directly, so choose whichever fits your use case.&lt;/p>
&lt;p>Here I use Claude MCP as an example.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">claude mcp add exa -e &lt;span class="nv">EXA_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>YOUR_API_KEY -- npx -y exa-mcp-server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For the specific MCP server configuration, refer to the example below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;exa&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;-y&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;exa-mcp-server&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;EXA_API_KEY&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;xxxxxx&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here is how to let the AI call MCP. The method is basically the same as other MCP servers.&lt;/p>
&lt;ol>
&lt;li>The simplest way is to tell Claude directly, for example: &lt;code>Use Exa to search how to preview images in a WeChat Mini Program&lt;/code>. Then you can see a relatively high-quality response, and the response speed is indeed fast.&lt;/li>
&lt;li>You can also define in the project prompt that online search should use Exa MCP. Then when the AI decides which tool to call, it will use Exa MCP instead of the built-in web search.&lt;/li>
&lt;/ol>
&lt;p>Which one to choose depends on your workflow.&lt;/p>
&lt;p>Aside from MCP, the SDK or API usage is straightforward. Just follow the documentation.&lt;/p>
&lt;h2 id="related-docs">
&lt;a class="heading-anchor-link" href="#related-docs">Related Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://exa.ai/docs/reference/search-api-guide" target="_blank" rel="noopener">https://exa.ai/docs/reference/search-api-guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>If you have not tried Exa before, I recommend giving it a shot. Personally, I think it is very good.&lt;/li>
&lt;/ol></description></item><item><title>Understanding OneKey Hardware Wallets</title><link>https://en.1991421.cn/2026/03/25/understanding-onekey-hardware-wallet/</link><pubDate>Wed, 25 Mar 2026 00:00:00 +0800</pubDate><guid>https://en.1991421.cn/2026/03/25/understanding-onekey-hardware-wallet/</guid><description>&lt;blockquote>
&lt;p>I recently needed to research hardware wallets for work. After looking at Ledger, I also wanted to understand OneKey, since it is another mainstream option and worth considering in a technical evaluation.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://asset.onekey.so/portal/fd46230d4f5f718b806fae142eac1345d41abccd/static/1e313eca409cdcddb7f4496c6e4af015/7e8b6/intro.webp"
alt="OneKey hardware wallet intro"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="supporting-a-custom-blockchain">
&lt;a class="heading-anchor-link" href="#supporting-a-custom-blockchain">Supporting a Custom Blockchain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supporting-a-custom-blockchain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you want a custom blockchain to use OneKey for secure storage and signing, there are two paths:&lt;/p>
&lt;ol>
&lt;li>Develop an app under the &lt;a href="https://github.com/OneKeyHQ/firmware" target="_blank" rel="noopener">firmware&lt;/a> repository, submit a PR, and once merged, the new chain is supported after users upgrade their firmware.&lt;/li>
&lt;li>Use an official SDK such as the Web SDK to connect the wallet in the appropriate context.&lt;/li>
&lt;/ol>
&lt;h2 id="how-end-users-use-it">
&lt;a class="heading-anchor-link" href="#how-end-users-use-it">How End Users Use It&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-end-users-use-it"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>There is no concept of &amp;ldquo;installing an app&amp;rdquo; on OneKey. All the user can do is upgrade the firmware.&lt;/li>
&lt;/ol>
&lt;h2 id="ledger-vs-onekey-comparison">
&lt;a class="heading-anchor-link" href="#ledger-vs-onekey-comparison">Ledger vs OneKey Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ledger-vs-onekey-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Ledger requires installing an app on the device; OneKey does not, but it requires a firmware upgrade. You can think of Ledger as an app store model and OneKey as an OS model.&lt;/li>
&lt;li>Ledger app submission goes through a store listing request. OneKey support is added via a GitHub PR on the open-source firmware repo — once the PR is merged, it is done.&lt;/li>
&lt;li>In development, both use a simulator for non-device testing. For real hardware, Ledger has restrictions and requires a model that supports sideloading, while OneKey has no such restriction, though it still requires a firmware upgrade.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.onekey.so/zh/" target="_blank" rel="noopener">https://developer.onekey.so/zh/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Understanding Ledger Hardware Wallets</title><link>https://en.1991421.cn/2026/03/20/understanding-ledger-hardware-wallet/</link><pubDate>Fri, 20 Mar 2026 09:42:15 +0800</pubDate><guid>https://en.1991421.cn/2026/03/20/understanding-ledger-hardware-wallet/</guid><description>&lt;blockquote>
&lt;p>Ledger &lt;code>/'ledʒər/&lt;/code> is a hardware wallet brand. I recently needed to verify the signing flow for a new blockchain, so I tested it with a Ledger Nano X device.&lt;/p>
&lt;p>This post records the process and the main technical points.&lt;/p>
&lt;/blockquote>
&lt;h2 id="buying-a-ledger">
&lt;a class="heading-anchor-link" href="#buying-a-ledger">Buying a Ledger&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="buying-a-ledger"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The one I have was purchased by my company. If you want to get your own, the official store is the simplest option: &lt;a href="https://shop.ledger.com/?r=aadf58e8f8b8" target="_blank" rel="noopener">Ledger Shop&lt;/a>&lt;/p>
&lt;h2 id="what-it-is">
&lt;a class="heading-anchor-link" href="#what-it-is">What It Is&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-it-is"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A Ledger device stores private keys securely through physical isolation and a secure element chip.&lt;/p>
&lt;h2 id="what-it-does">
&lt;a class="heading-anchor-link" href="#what-it-does">What It Does&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-it-does"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Private key storage without exposing the key to the computer or phone&lt;/li>
&lt;li>Transaction signing with the signing action completed on the device itself&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://www.cashbackisl.com/wp-content/uploads/2026/02/copertina-27426-1.webp"
alt="Ledger hardware wallet"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="example-ethereum-signing-flow">
&lt;a class="heading-anchor-link" href="#example-ethereum-signing-flow">Example: Ethereum Signing Flow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-ethereum-signing-flow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="mermaid">sequenceDiagram
box Client
participant U as User
participant D as dApp
participant M as MetaMask
end
box External Signing Device
participant L as Ledger Device (Ethereum App)
end
box Blockchain Infrastructure
participant R as RPC / Ethereum Network
end
U->>D: Start transfer / approve / swap
D->>M: Ask wallet to create a transaction
M->>R: Fetch nonce / gas / fee / chainId
R-->>M: Return on-chain parameters
M->>U: Show transaction details
U->>M: Confirm in MetaMask
M->>L: Send unsigned transaction data
L->>U: Show key details on the device screen
U->>L: Confirm or reject with hardware buttons
alt User confirms
L-->>M: Return signature
M->>R: Broadcast signed transaction
R-->>M: Return tx hash / pending / confirmed
M-->>U: Show transaction status
else User rejects
L-->>M: Reject signing
M-->>U: Transaction canceled / failed
end
&lt;/div>
&lt;h2 id="supporting-a-custom-blockchain">
&lt;a class="heading-anchor-link" href="#supporting-a-custom-blockchain">Supporting a Custom Blockchain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supporting-a-custom-blockchain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you want a custom chain to use Ledger for secure key storage and signing, there are two main parts:&lt;/p>
&lt;ol>
&lt;li>Build a &lt;code>Ledger Device App&lt;/code> that runs on the hardware device and knows how to sign transactions for your chain.&lt;/li>
&lt;li>Build a plugin or integration layer that connects MetaMask or another wallet to that device app.
If your stack uses libraries such as &lt;code>web3.js&lt;/code>, you may also need to extend those libraries to support signing through the Ledger device app.&lt;/li>
&lt;/ol>
&lt;h3 id="call-chain--data-flow">
&lt;a class="heading-anchor-link" href="#call-chain--data-flow">Call Chain / Data Flow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="call-chain--data-flow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Your code
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Ledger SDK (wraps APDU)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">WebHID (transport)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Ledger device
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Your ChainMaker App (parses APDU)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>HID&lt;/code> means human interface device, and &lt;code>APDU&lt;/code> means application protocol data unit.&lt;/p>
&lt;h3 id="ledger-device-app-requirements">
&lt;a class="heading-anchor-link" href="#ledger-device-app-requirements">Ledger Device App Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ledger-device-app-requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>The device app is typically written in &lt;code>C&lt;/code> or &lt;code>Rust&lt;/code> because device resources are limited.
&lt;ul>
&lt;li>Rust template: &lt;a href="https://github.com/LedgerHQ/app-boilerplate-rust" target="_blank" rel="noopener">https://github.com/LedgerHQ/app-boilerplate-rust&lt;/a>&lt;/li>
&lt;li>C template: &lt;a href="https://github.com/LedgerHQ/app-boilerplate" target="_blank" rel="noopener">https://github.com/LedgerHQ/app-boilerplate&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Install the official &lt;a href="https://marketplace.visualstudio.com/items?itemName=LedgerHQ.ledger-dev-tools" target="_blank" rel="noopener">VS Code extension&lt;/a> for development and debugging.&lt;/li>
&lt;li>Building the device app requires Docker because Ledger provides a containerized toolchain.&lt;/li>
&lt;li>For simulator-based testing, install &lt;a href="https://github.com/LedgerHQ/speculos" target="_blank" rel="noopener">Speculos&lt;/a>.&lt;/li>
&lt;/ol>
&lt;h3 id="vs-code-extension">
&lt;a class="heading-anchor-link" href="#vs-code-extension">VS Code Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="vs-code-extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The extension provides a solid development, build, and debug workflow, which helps a lot when iterating on a device app.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/03/2026-03-24-144706.jpeg"
alt="Ledger VS Code tools screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="real-device-vs-simulator">
&lt;a class="heading-anchor-link" href="#real-device-vs-simulator">Real Device vs Simulator&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="real-device-vs-simulator"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Item&lt;/th>
&lt;th style="text-align: left">Real Device (WebHID)&lt;/th>
&lt;th style="text-align: left">Simulator (Speculos)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Connection&lt;/td>
&lt;td style="text-align: left">&lt;code>TransportWebHID.create()&lt;/code> with browser HID picker&lt;/td>
&lt;td style="text-align: left">&lt;code>SpeculosHttpTransport.open(url)&lt;/code> over local HTTP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Protocol&lt;/td>
&lt;td style="text-align: left">USB HID frames via the browser WebHID API&lt;/td>
&lt;td style="text-align: left">HTTP JSON such as &lt;code>{ data: &amp;quot;hex...&amp;quot; }&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">User confirmation&lt;/td>
&lt;td style="text-align: left">Physical button confirmation is required&lt;/td>
&lt;td style="text-align: left">Manual confirmation in Speculos&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Signer setup&lt;/td>
&lt;td style="text-align: left">Usually pass &lt;code>signDigest&lt;/code> and &lt;code>fetchPublicKey&lt;/code> explicitly&lt;/td>
&lt;td style="text-align: left">Defaults are often enough&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Later flow&lt;/td>
&lt;td style="text-align: left">&lt;code>isReady()&lt;/code> -&amp;gt; &lt;code>fetchPublicKey()&lt;/code> -&amp;gt; &lt;code>signMessage()&lt;/code> -&amp;gt; submit on-chain&lt;/td>
&lt;td style="text-align: left">Same overall flow&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="submission-and-release">
&lt;a class="heading-anchor-link" href="#submission-and-release">Submission and Release&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="submission-and-release"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>What you submit is not a binary package. You submit the app source repository plus the required deliverables. Ledger then forks your app repository and manages deployment in their own release flow.&lt;/p>
&lt;p>Typical process:&lt;/p>
&lt;ol>
&lt;li>Contact the Ledger team and submit the project form:
&lt;a href="https://developers.ledger.com/docs/device-app/submission-process/submission-form" target="_blank" rel="noopener">https://developers.ledger.com/docs/device-app/submission-process/submission-form&lt;/a>&lt;/li>
&lt;li>Make sure the code meets Ledger&amp;rsquo;s security and cryptography requirements.&lt;/li>
&lt;li>Prepare deliverables:
&lt;ul>
&lt;li>The app source repository&lt;/li>
&lt;li>&lt;code>docs/apdu.md&lt;/code> describing APDU commands and status words&lt;/li>
&lt;li>User documentation&lt;/li>
&lt;li>Device compatibility information for Nano S Plus, Nano X, Stax, and Flex&lt;/li>
&lt;li>Legal entity information and contact details&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Submit the official device app or plugin form.&lt;/li>
&lt;li>Pass the security audit before Ledger publishes the app.&lt;/li>
&lt;/ol>
&lt;p>This review process is heavy and can take weeks or even months.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/03/2026-03-30-161301.jpeg"
alt="Ledger submission and release flow"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="how-end-users-use-it">
&lt;a class="heading-anchor-link" href="#how-end-users-use-it">How End Users Use It&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-end-users-use-it"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The user installs Ledger software and sets up the device.&lt;/li>
&lt;li>The user installs the target app onto the Ledger device.
&lt;ul>
&lt;li>If the app has not been officially published yet, you usually sideload it through the official VS Code tools.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/03/2026-03-24-171254.jpeg"
alt="Ledger app sideload example"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="other-notes">
&lt;a class="heading-anchor-link" href="#other-notes">Other Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="why-are-there-so-many-models-and-why-are-they-expensive">
&lt;a class="heading-anchor-link" href="#why-are-there-so-many-models-and-why-are-they-expensive">Why are there so many models, and why are they expensive?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-are-there-so-many-models-and-why-are-they-expensive"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>A hardware wallet is more than just a security chip.&lt;/li>
&lt;li>Different models trade off security level, user experience, storage capacity, and use case, so the pricing differs naturally.&lt;/li>
&lt;/ol>
&lt;h3 id="about-navigatorhid">
&lt;a class="heading-anchor-link" href="#about-navigatorhid">About &lt;code>navigator.hid&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="about-navigatorhid"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>navigator.hid&lt;/code> (WebHID) connects to physical USB HID devices. Speculos is only a simulator that exposes TCP sockets and an HTTP API. It is not a real HID device, so the browser cannot discover it through WebHID scanning.&lt;/p>
&lt;h2 id="contact-ledger">
&lt;a class="heading-anchor-link" href="#contact-ledger">Contact Ledger&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="contact-ledger"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://discord.com/invite/ledger" target="_blank" rel="noopener">Discord&lt;/a>&lt;/li>
&lt;li>Email: &lt;a href="mailto:hello@ledger.fr">hello@ledger.fr&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developers.ledger.com/docs/device-app/integration/how-to/app-boilerplate" target="_blank" rel="noopener">https://developers.ledger.com/docs/device-app/integration/how-to/app-boilerplate&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.ledger.com/docs/device-app/beginner/vscode-extension" target="_blank" rel="noopener">https://developers.ledger.com/docs/device-app/beginner/vscode-extension&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.ledger.com/docs/device-app/submission-process/submission-form" target="_blank" rel="noopener">https://developers.ledger.com/docs/device-app/submission-process/submission-form&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.ledger.com/docs/device-interaction/integration/how_to/transports" target="_blank" rel="noopener">https://developers.ledger.com/docs/device-interaction/integration/how_to/transports&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.cashbackisl.com/ledger-cold-wallet-guide-hk/" target="_blank" rel="noopener">https://www.cashbackisl.com/ledger-cold-wallet-guide-hk/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ledger-app-guide.com/desktop/index.html" target="_blank" rel="noopener">https://ledger-app-guide.com/desktop/index.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://shop.ledger.com/pages/ledger-wallet" target="_blank" rel="noopener">https://shop.ledger.com/pages/ledger-wallet&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Hardware Requirements for Deploying Openclaw</title><link>https://en.1991421.cn/2026/03/06/openclaw-deployment-hardware-requirements/</link><pubDate>Fri, 06 Mar 2026 17:48:36 +0800</pubDate><guid>https://en.1991421.cn/2026/03/06/openclaw-deployment-hardware-requirements/</guid><description>&lt;blockquote>
&lt;p>A friend recently asked me: what kind of machine do you actually need to deploy OpenClaw (formerly ClawdBot)? Bottom line up front: it&amp;rsquo;s very lightweight, and hardware isn&amp;rsquo;t the bottleneck. A low-spec machine can run it, but whether you run it &lt;em>stably and fast&lt;/em> depends on your specific use case.&lt;/p>
&lt;/blockquote>
&lt;h2 id="openclaw-minimum-requirements">
&lt;a class="heading-anchor-link" href="#openclaw-minimum-requirements">OpenClaw Minimum Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="openclaw-minimum-requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>OpenClaw is officially positioned as &amp;ldquo;low barrier to deploy.&amp;rdquo; The following specs serve as a baseline:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>CPU:&lt;/strong> 2+ cores (Intel N-series, entry-level AMD Ryzen, or ARM all work)&lt;/li>
&lt;li>&lt;strong>RAM:&lt;/strong> 4GB (basic chat functionality can go as low as 2GB)&lt;/li>
&lt;li>&lt;strong>Storage:&lt;/strong> At least 2GB free disk space&lt;/li>
&lt;li>&lt;strong>Runtime:&lt;/strong> Node.js v22+&lt;/li>
&lt;li>&lt;strong>OS:&lt;/strong> Linux, Windows (via WSL2), or macOS 13+&lt;/li>
&lt;li>&lt;strong>Network:&lt;/strong> Stable internet connection; if using overseas models, make sure your network can reach them&lt;/li>
&lt;/ul>
&lt;h2 id="recommended-specs">
&lt;a class="heading-anchor-link" href="#recommended-specs">Recommended Specs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-specs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For more stable long-term operation:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RAM:&lt;/strong> 16GB or more&lt;/li>
&lt;li>&lt;strong>Storage:&lt;/strong> 256GB SSD (improves system responsiveness and I/O)&lt;/li>
&lt;li>&lt;strong>CPU:&lt;/strong> 4+ cores (better experience for concurrent tasks and browser automation)&lt;/li>
&lt;/ul>
&lt;p>The baseline gets you &amp;ldquo;can it run,&amp;rdquo; while the recommended specs determine &amp;ldquo;will it run without frequent freezes or disconnects.&amp;rdquo;&lt;/p>
&lt;h2 id="deployment-options">
&lt;a class="heading-anchor-link" href="#deployment-options">Deployment Options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="deployment-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-cloud">
&lt;a class="heading-anchor-link" href="#1-cloud">1) Cloud&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-cloud"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Budget VPS:&lt;/strong> Starting around $5/month; domestic cloud providers work too.&lt;/li>
&lt;li>&lt;strong>Free option:&lt;/strong> Oracle Cloud free tier (4 ARM cores + 24GB RAM available).&lt;/li>
&lt;/ul>
&lt;p>Best for:&lt;/p>
&lt;ul>
&lt;li>24/7 availability&lt;/li>
&lt;li>Not tying up your local machine&lt;/li>
&lt;/ul>
&lt;h3 id="2-local-deployment">
&lt;a class="heading-anchor-link" href="#2-local-deployment">2) Local Deployment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-local-deployment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Can use Docker&lt;/li>
&lt;li>Or install Node.js directly on the host and run it; personally, I deploy on a Mac mini at home.&lt;/li>
&lt;/ul>
&lt;p>Best for:&lt;/p>
&lt;ul>
&lt;li>Frequent debugging and development&lt;/li>
&lt;li>Data locality requirements&lt;/li>
&lt;/ul>
&lt;h2 id="when-you-need-more-power">
&lt;a class="heading-anchor-link" href="#when-you-need-more-power">When You Need More Power&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-you-need-more-power"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>OpenClaw itself is lightweight, but these scenarios significantly raise hardware demands:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Running local LLMs:&lt;/strong> At least 8GB–16GB RAM recommended&lt;/li>
&lt;li>&lt;strong>Browser automation enabled:&lt;/strong> 16GB RAM recommended for a stable experience&lt;/li>
&lt;li>&lt;strong>High concurrency&lt;/strong> (scraping + automation + chat simultaneously): Leave headroom in both CPU and RAM&lt;/li>
&lt;/ul>
&lt;h2 id="practical-advice">
&lt;a class="heading-anchor-link" href="#practical-advice">Practical Advice&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practical-advice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you&amp;rsquo;re just trying it out, 2 cores + 4GB RAM + Node.js 22+ is enough to get started.&lt;/p>
&lt;p>If you plan to run automation tasks long-term, go straight for 4 cores + 16GB + SSD — you&amp;rsquo;ll save a lot of troubleshooting and restart time down the road.&lt;/p>
&lt;p>Also, the installation process typically supports a one-liner CLI command. Get it running first, then scale up based on actual load — that&amp;rsquo;s the most efficient approach.&lt;/p></description></item><item><title>OpenClaw MCP Setup Guide: How to Connect Third-Party Tools to Your AI Assistant</title><link>https://en.1991421.cn/2026/03/04/how-to-use-mcp-in-openclaw/</link><pubDate>Wed, 04 Mar 2026 17:35:58 +0800</pubDate><guid>https://en.1991421.cn/2026/03/04/how-to-use-mcp-in-openclaw/</guid><description>&lt;blockquote>
&lt;p>I needed to call TAPD MCP in OpenClaw, but found that it doesn&amp;rsquo;t natively support MCP. A third-party solution is required. The official recommendation is to use MCPorter as a middleware layer.&lt;/p>
&lt;/blockquote>
&lt;h2 id="install-mcporter">
&lt;a class="heading-anchor-link" href="#install-mcporter">Install MCPorter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-mcporter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm i mcporter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">which uvx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -LsSf https://astral.sh/uv/install.sh | sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">source ~/.bashrc # or source ~/.profile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="add-mcp">
&lt;a class="heading-anchor-link" href="#add-mcp">Add MCP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-mcp"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>npx mcporter config add mcp-server-tapd &lt;br>
&amp;ndash;stdio &amp;ldquo;uvx mcp-server-tapd&amp;rdquo; &lt;br>
&amp;ndash;env TAPD_ACCESS_TOKEN=xxxx &lt;br>
&amp;ndash;env TAPD_API_BASE_URL=https://api.tapd.cn &lt;br>
&amp;ndash;env TAPD_BASE_URL=https://www.tapd.cn&lt;/p>
&lt;p>The config file is located at &lt;code>/root/config/mcporter.json&lt;/code>.&lt;/p>
&lt;h2 id="view">
&lt;a class="heading-anchor-link" href="#view">View&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="view"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Run this command to check the MCP status:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">mcporter list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-reading">
&lt;a class="heading-anchor-link" href="#related-reading">Related Reading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-reading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.1991421.cn/2026/02/20/play-with-openclaw/">OpenClaw Getting Started&lt;/a> - installation, configuration, Telegram Bot integration, and MCP basics&lt;/li>
&lt;li>&lt;a href="https://en.1991421.cn/2026/03/04/openclaw-memory-management-and-invocation/">OpenClaw Memory Management and Invocation&lt;/a> - global memory via &lt;code>SOUL.md&lt;/code> and &lt;code>AGENTS.md&lt;/code>&lt;/li>
&lt;li>&lt;a href="https://en.1991421.cn/2026/03/11/openclaw-implement-social-media-publishing/">OpenClaw for Social Media Publishing&lt;/a> - using MCP to publish to WeChat, Xiaohongshu, and Toutiao&lt;/li>
&lt;/ul>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When using it, you can explicitly tell OpenClaw to use the mcp-server-tapd under MCPorter at the beginning. Once it&amp;rsquo;s saved to memory, you can simplify it afterward by just sending the ticket link directly.&lt;/p></description></item><item><title>Using Tapd MCP</title><link>https://en.1991421.cn/2026/03/03/using-tapd-mcp/</link><pubDate>Tue, 03 Mar 2026 17:08:22 +0800</pubDate><guid>https://en.1991421.cn/2026/03/03/using-tapd-mcp/</guid><description>&lt;blockquote>
&lt;p>TAPD is used quite frequently at work, and I&amp;rsquo;ve recently been integrating MCP. Here, using Claude Code as an example, I&amp;rsquo;ll list how to set it up. Other AI tools work similarly.&lt;/p>
&lt;/blockquote>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Note: You can use either a token or username/password, whichever you prefer. The token can be added at &lt;a href="https://www.tapd.cn/personal_settings/index?tab=personal_token" target="_blank" rel="noopener">this location&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcp-server-tapd&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;stdio&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;uvx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;mcp-server-tapd&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;TAPD_ACCESS_TOKEN&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;xxxx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;TAPD_API_USER&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;TAPD_API_PASSWORD&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;TAPD_API_BASE_URL&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://api.tapd.cn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;TAPD_BASE_URL&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://www.tapd.cn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;BOT_URL&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="testing">
&lt;a class="heading-anchor-link" href="#testing">Testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After setup, enter a conversation in Claude and use &lt;code>/mcp&lt;/code> to check the connection status of the Tapd MCP.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://cloud.tencent.com/developer/mcp/server/11474" target="_blank" rel="noopener">https://cloud.tencent.com/developer/mcp/server/11474&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Complete Guide to Migrating Tencent Cloud Static Resources to Cloudflare R2</title><link>https://en.1991421.cn/2026/02/01/migrate-tencent-cloud-static-resources-to-cloudflare-r2/</link><pubDate>Sun, 01 Feb 2026 17:41:35 +0800</pubDate><guid>https://en.1991421.cn/2026/02/01/migrate-tencent-cloud-static-resources-to-cloudflare-r2/</guid><description>&lt;blockquote>
&lt;p>Due to increasing dissatisfaction with domestic cloud vendors and to improve blog service performance, I&amp;rsquo;m migrating the blog&amp;rsquo;s static resources from Tencent Cloud to Cloudflare R2. The steps are quite tedious, including rclone configuration, Worker deployment, and custom domain binding. However, with AI assistance, the entire migration process took about 1 hour, and 0.5GB of static resources were switched over with zero downtime. Here&amp;rsquo;s a complete record of the steps for everyone&amp;rsquo;s reference.&lt;/p>
&lt;/blockquote>
&lt;h2 id="background-and-goals">
&lt;a class="heading-anchor-link" href="#background-and-goals">Background and Goals&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background-and-goals"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The main purposes of migrating static resources from Tencent Cloud to Cloudflare R2 are:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Cost Reduction&lt;/strong>: Cloudflare R2 has completely free egress traffic. In the future, I plan to abandon services that rely on domestic clouds, as they are full of rogue practices from login to use.&lt;/li>
&lt;li>&lt;strong>Performance Improvement&lt;/strong>: Utilize Cloudflare&amp;rsquo;s global CDN acceleration to optimize blog access speed and SEO.&lt;/li>
&lt;/ol>
&lt;h2 id="final-architecture">
&lt;a class="heading-anchor-link" href="#final-architecture">Final Architecture&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-architecture"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Server&lt;/span> &lt;span class="n">local&lt;/span> &lt;span class="n">directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">www&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">blog&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">static&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">assets&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">↓&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">rclone&lt;/span> &lt;span class="n">sync&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Cloudflare&lt;/span> &lt;span class="n">R2&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">private&lt;/span> &lt;span class="n">bucket&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">↓&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Worker&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">1991421.&lt;/span>&lt;span class="n">cn&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">custom&lt;/span> &lt;span class="n">domain&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Architecture Features&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Blog URLs remain unchanged&lt;/li>
&lt;li>R2 Bucket remains private&lt;/li>
&lt;li>Access controlled via Worker&lt;/li>
&lt;li>Zero egress traffic costs&lt;/li>
&lt;/ul>
&lt;h2 id="1-install-rclone">
&lt;a class="heading-anchor-link" href="#1-install-rclone">1. Install rclone&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-install-rclone"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Install rclone on the server:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl https://rclone.org/install.sh &lt;span class="p">|&lt;/span> sudo bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-configure-rclone-to-connect-to-r2">
&lt;a class="heading-anchor-link" href="#2-configure-rclone-to-connect-to-r2">2. Configure rclone to Connect to R2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-configure-rclone-to-connect-to-r2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-start-configuration-wizard">
&lt;a class="heading-anchor-link" href="#1-start-configuration-wizard">1. Start Configuration Wizard&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-start-configuration-wizard"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rclone config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-create-a-new-remote">
&lt;a class="heading-anchor-link" href="#2-create-a-new-remote">2. Create a New Remote&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-create-a-new-remote"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Configure according to the following steps:&lt;/p>
&lt;ol>
&lt;li>Select &lt;code>n&lt;/code> to create a new remote&lt;/li>
&lt;li>Name: &lt;code>cf-r2&lt;/code>&lt;/li>
&lt;li>Storage type: &lt;code>s3&lt;/code> (S3 compatible object storage)&lt;/li>
&lt;li>Provider: &lt;code>47&lt;/code> (Any other S3 compatible provider)&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>&lt;strong>Why not select Cloudflare R2 option?&lt;/strong> R2 is not in the built-in vendor list, so we must use the generic S3 compatible configuration.&lt;/p>
&lt;/blockquote>
&lt;h3 id="3-fill-in-authentication-information">
&lt;a class="heading-anchor-link" href="#3-fill-in-authentication-information">3. Fill in Authentication Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-fill-in-authentication-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After creating an API token in the Cloudflare R2 console, you will see three fields. Here&amp;rsquo;s the corresponding relationship:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Cloudflare Console Field&lt;/th>
&lt;th style="text-align: left">rclone Config Item&lt;/th>
&lt;th style="text-align: left">Required?&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Access Key ID&lt;/td>
&lt;td style="text-align: left">&lt;code>access_key_id&lt;/code>&lt;/td>
&lt;td style="text-align: left">✅ Required&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Secret Access Key&lt;/td>
&lt;td style="text-align: left">&lt;code>secret_access_key&lt;/code>&lt;/td>
&lt;td style="text-align: left">✅ Required&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Token Value&lt;/td>
&lt;td style="text-align: left">-&lt;/td>
&lt;td style="text-align: left">❌ Ignore&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Enter in rclone:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">access_key_id&amp;gt; [Your Access Key ID]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">secret_access_key&amp;gt; [Your Secret Access Key]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-configure-connection-parameters">
&lt;a class="heading-anchor-link" href="#4-configure-connection-parameters">4. Configure Connection Parameters&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="4-configure-connection-parameters"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;strong>Region&lt;/strong> (leave empty):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">region&amp;gt; [Press Enter directly]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>R2 doesn&amp;rsquo;t have a region concept, leaving it empty is the official recommended approach.&lt;/p>
&lt;p>&lt;strong>Endpoint&lt;/strong> (required):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">endpoint&amp;gt; https://&amp;lt;AccountID&amp;gt;.r2.cloudflarestorage.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Example: &lt;code>https://xxxxx.r2.cloudflarestorage.com&lt;/code>&lt;/p>
&lt;p>You can find this address on the Bucket details page in the R2 console.&lt;/p>
&lt;p>&lt;strong>Location Constraint&lt;/strong> (leave empty):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">location_constraint&amp;gt; [Press Enter directly]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>ACL&lt;/strong> (empty = private):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">acl&amp;gt; [Press Enter directly]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>R2 doesn&amp;rsquo;t use S3 ACLs; permissions are managed by Workers.&lt;/p>
&lt;p>&lt;strong>Advanced config&lt;/strong> (not needed):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Edit advanced config? n
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="5-confirm-configuration">
&lt;a class="heading-anchor-link" href="#5-confirm-configuration">5. Confirm Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="5-confirm-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">y) Yes this is OK
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Configuration complete!&lt;/p>
&lt;h2 id="3-perform-data-migration">
&lt;a class="heading-anchor-link" href="#3-perform-data-migration">3. Perform Data Migration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-perform-data-migration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="initial-migration-recommended-copy">
&lt;a class="heading-anchor-link" href="#initial-migration-recommended-copy">Initial Migration (Recommended: copy)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="initial-migration-recommended-copy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rclone copy /var/www/blog/static-assets cf-r2:static-assets &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --progress &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --transfers&lt;span class="o">=&lt;/span>&lt;span class="m">16&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --checkers&lt;span class="o">=&lt;/span>&lt;span class="m">16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>copy&lt;/code> command only copies files and doesn&amp;rsquo;t delete files on the destination, making it safer.&lt;/p>
&lt;h3 id="daily-synchronization-use-sync">
&lt;a class="heading-anchor-link" href="#daily-synchronization-use-sync">Daily Synchronization (Use sync)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="daily-synchronization-use-sync"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rclone sync /var/www/blog/static-assets cf-r2:static-assets &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --progress &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --transfers&lt;span class="o">=&lt;/span>&lt;span class="m">16&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --checkers&lt;span class="o">=&lt;/span>&lt;span class="m">16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>sync&lt;/code> makes the destination exactly match the source and will delete excess files on the destination.&lt;/p>
&lt;p>&lt;strong>Parameter Explanations&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--progress&lt;/code>: Show transfer progress&lt;/li>
&lt;li>&lt;code>--transfers=16&lt;/code>: Transfer 16 files concurrently&lt;/li>
&lt;li>&lt;code>--checkers=16&lt;/code>: Check 16 files concurrently&lt;/li>
&lt;/ul>
&lt;h2 id="4-configure-cloudflare-r2-bucket">
&lt;a class="heading-anchor-link" href="#4-configure-cloudflare-r2-bucket">4. Configure Cloudflare R2 Bucket&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="4-configure-cloudflare-r2-bucket"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Create an R2 Bucket in the Cloudflare console:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Bucket Name&lt;/strong>: &lt;code>static-assets&lt;/code>&lt;/li>
&lt;li>&lt;strong>Storage Type&lt;/strong>: Standard&lt;/li>
&lt;li>&lt;strong>Region&lt;/strong>: Auto&lt;/li>
&lt;li>&lt;strong>Public Access&lt;/strong>: ❌ Disabled (keep private)&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;strong>Important&lt;/strong>: Do not enable public access; we will control access through Workers.&lt;/p>
&lt;/blockquote>
&lt;h2 id="5-deploy-cloudflare-worker">
&lt;a class="heading-anchor-link" href="#5-deploy-cloudflare-worker">5. Deploy Cloudflare Worker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="5-deploy-cloudflare-worker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-create-a-worker">
&lt;a class="heading-anchor-link" href="#1-create-a-worker">1. Create a Worker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-create-a-worker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In the Cloudflare console:&lt;/p>
&lt;ol>
&lt;li>Go to &lt;strong>Workers &amp;amp; Pages&lt;/strong>&lt;/li>
&lt;li>Click &lt;strong>Create Application&lt;/strong>&lt;/li>
&lt;li>Select &lt;strong>Create Worker&lt;/strong>&lt;/li>
&lt;li>Select &lt;strong>Start from Hello World&lt;/strong>&lt;/li>
&lt;/ol>
&lt;h3 id="2-write-worker-code">
&lt;a class="heading-anchor-link" href="#2-write-worker-code">2. Write Worker Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-write-worker-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Paste the following code into the Worker editor:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">async&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">env&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">URL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pathname&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">slice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Not Found&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">status&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">404&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">object&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">STATIC_ASSETS&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">object&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Not Found&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">status&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">404&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">headers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">httpMetadata&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">contentType&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="s1">&amp;#39;application/octet-stream&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Cache-Control&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;public, max-age=31536000, immutable&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Code Explanation&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Extract file key from URL path&lt;/li>
&lt;li>Get object from R2&lt;/li>
&lt;li>Set correct Content-Type&lt;/li>
&lt;li>Add long-term cache headers (1 year)&lt;/li>
&lt;/ul>
&lt;h3 id="3-bind-r2-bucket">
&lt;a class="heading-anchor-link" href="#3-bind-r2-bucket">3. Bind R2 Bucket&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-bind-r2-bucket"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This is the most critical step and is easily overlooked!&lt;/p>
&lt;p>In Worker settings:&lt;/p>
&lt;ol>
&lt;li>Go to &lt;strong>Settings &amp;gt; Variables and Secrets&lt;/strong>&lt;/li>
&lt;li>Select &lt;strong>R2 Bucket Bindings&lt;/strong> (not environment variables!)&lt;/li>
&lt;li>Add binding:
&lt;ul>
&lt;li>&lt;strong>Variable Name&lt;/strong>: &lt;code>STATIC_ASSETS&lt;/code>&lt;/li>
&lt;li>&lt;strong>R2 Bucket&lt;/strong>: Select &lt;code>static-assets&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>&lt;strong>Note&lt;/strong>: Must use R2 Bucket binding; do not use environment variables or plain text.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/02/2026-02-01-201646.jpeg"
alt="https://static.1991421.cn/2026/02/2026-02-01-201646.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="4-test-worker">
&lt;a class="heading-anchor-link" href="#4-test-worker">4. Test Worker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="4-test-worker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After deployment, test using the workers.dev domain:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -I https://static-assets.your-account.workers.dev/2018-06-06-Validate.jpg
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If it returns the image correctly, the Worker to R2 connection is established.&lt;/p>
&lt;h2 id="6-bind-custom-domain">
&lt;a class="heading-anchor-link" href="#6-bind-custom-domain">6. Bind Custom Domain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="6-bind-custom-domain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-add-custom-domain">
&lt;a class="heading-anchor-link" href="#1-add-custom-domain">1. Add Custom Domain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-add-custom-domain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In Worker settings:&lt;/p>
&lt;ol>
&lt;li>Go to &lt;strong>Triggers&lt;/strong>&lt;/li>
&lt;li>Click &lt;strong>Add Custom Domain&lt;/strong>&lt;/li>
&lt;li>Enter domain: &lt;code>static.1991421.cn&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>If the domain is already managed by Cloudflare:&lt;/p>
&lt;ul>
&lt;li>DNS records will be created automatically&lt;/li>
&lt;li>HTTPS will be enabled automatically&lt;/li>
&lt;li>Usually takes effect within 1 minute&lt;/li>
&lt;/ul>
&lt;h3 id="2-verify-domain">
&lt;a class="heading-anchor-link" href="#2-verify-domain">2. Verify Domain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-verify-domain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -I https://static.1991421.cn/2018-06-06-Validate.jpg
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Check response headers:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">server: cloudflare
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cache-control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These indicate that it&amp;rsquo;s officially online!&lt;/p>
&lt;h2 id="7-update-blog-references">
&lt;a class="heading-anchor-link" href="#7-update-blog-references">7. Update Blog References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="7-update-blog-references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you used a new domain, you need to update the static resource URLs in your blog. In my case, they remain unchanged, so this step isn&amp;rsquo;t necessary. If you need to update, you can use batch replacement:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Old: https://old-domain.com/image.jpg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">New: https://static.1991421.cn/image.jpg
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can use a batch replacement tool to complete this.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This migration achieved:&lt;/p>
&lt;p>✅ &lt;strong>Cost Optimization&lt;/strong>: R2 egress traffic is $0
✅ &lt;strong>Performance Improvement&lt;/strong>: Cloudflare global CDN acceleration
✅ &lt;strong>Architecture Optimization&lt;/strong>: Standard practice of Private Bucket + Worker
✅ &lt;strong>Zero Downtime Migration&lt;/strong>: URLs remain unchanged, users are unaware&lt;/p>
&lt;p>The entire migration process took only &lt;code>1 hour&lt;/code>, and all 0.5GB of static resources were migrated. This is the Cloudflare R2 + Worker approach.&lt;/p></description></item><item><title>How to Use AI to Build a Full-Stack Project (Step-by-Step Guide)</title><link>https://en.1991421.cn/2026/01/22/using-ai-to-build-a-fullstack-project/</link><pubDate>Thu, 22 Jan 2026 21:59:44 +0800</pubDate><guid>https://en.1991421.cn/2026/01/22/using-ai-to-build-a-fullstack-project/</guid><description>&lt;blockquote>
&lt;p>You can spin up a solid full-stack project quickly with AI. To avoid back-and-forth, here’s the tech stack I rely on in real projects. Feed it to your AI or drop it in project docs—just hand it to the model.&lt;/p>
&lt;/blockquote>
&lt;h2 id="frontend-stack">
&lt;a class="heading-anchor-link" href="#frontend-stack">Frontend stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Package manager: npm&lt;/li>
&lt;li>React&lt;/li>
&lt;li>TypeScript&lt;/li>
&lt;li>Vite&lt;/li>
&lt;li>dayjs&lt;/li>
&lt;li>Tailwind CSS + ShaCDN Components&lt;/li>
&lt;li>Need charts? Use ECharts&lt;/li>
&lt;/ol>
&lt;h2 id="backend-stack">
&lt;a class="heading-anchor-link" href="#backend-stack">Backend stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="backend-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Package manager: npm&lt;/li>
&lt;li>NestJS&lt;/li>
&lt;li>TypeScript&lt;/li>
&lt;li>Database: MySQL&lt;/li>
&lt;li>ORM: TypeORM&lt;/li>
&lt;li>JWT for auth&lt;/li>
&lt;li>Swagger for API docs&lt;/li>
&lt;/ol>
&lt;h2 id="miniprogram-stack">
&lt;a class="heading-anchor-link" href="#miniprogram-stack">Mini‑program stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="miniprogram-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Package manager: npm&lt;/li>
&lt;li>Taro framework&lt;/li>
&lt;li>TypeScript&lt;/li>
&lt;li>Build with Vite&lt;/li>
&lt;li>dayjs for dates/times&lt;/li>
&lt;li>Component library: TDesign Mini&lt;/li>
&lt;/ol></description></item><item><title>Automating WeChat Mini Program Deployment with miniprogram-ci</title><link>https://en.1991421.cn/2026/01/15/using-miniprogram-ci-to-automatically-publish-wechat-mini-program/</link><pubDate>Thu, 15 Jan 2026 17:52:17 +0800</pubDate><guid>https://en.1991421.cn/2026/01/15/using-miniprogram-ci-to-automatically-publish-wechat-mini-program/</guid><description>&lt;blockquote>
&lt;p>Every time I release a new version of my mini program to the preview environment, I need to: build the package =&amp;gt; open WeChat Developer Tools =&amp;gt; click upload =&amp;gt; click submit. The whole process is tedious. This manual publishing workflow is extremely inefficient, so I wanted to automate it with a script. I discovered the official miniprogram-ci npm package, and after some exploration, I finally solved this pain point. However, the official documentation is quite sparse, so I&amp;rsquo;m documenting my solution here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="my-original-project-setup">
&lt;a class="heading-anchor-link" href="#my-original-project-setup">My Original Project Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="my-original-project-setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I&amp;rsquo;m using Taro, so before each release I need to run &lt;code>npm run build:weapp&lt;/code> to compile and build the project. The built files are located in &lt;code>/dist/weapp&lt;/code>, and this is the folder I need to upload and publish.&lt;/p>
&lt;h2 id="implementing-automated-publishing-with-miniprogram-ci">
&lt;a class="heading-anchor-link" href="#implementing-automated-publishing-with-miniprogram-ci">Implementing Automated Publishing with miniprogram-ci&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementing-automated-publishing-with-miniprogram-ci"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s the script code for publishing to the development version. Based on this code block, create a publish.js script. Integrating it with the build process is also straightforward.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="n">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">project&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">new&lt;/span> &lt;span class="n">ci&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">appid&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">type&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;miniProgram&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">projectPath&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;../&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;dist&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">privateKeyPath&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;private.key&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ignores&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;node_modules/**/*&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Common&lt;/span> &lt;span class="n">compilation&lt;/span> &lt;span class="n">settings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">setting&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Disable&lt;/span> &lt;span class="n">ES6&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">ES5&lt;/span> &lt;span class="n">conversion&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">as&lt;/span> &lt;span class="n">Taro&lt;/span> &lt;span class="n">already&lt;/span> &lt;span class="n">handles&lt;/span> &lt;span class="n">this&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">es6&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Disable&lt;/span> &lt;span class="n">enhanced&lt;/span> &lt;span class="n">compilation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">enhance&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Disable&lt;/span> &lt;span class="n">ES7&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">ES5&lt;/span> &lt;span class="n">conversion&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">es7&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Code&lt;/span> &lt;span class="n">protection&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">obfuscation&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">minified&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Auto&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">prefix&lt;/span> &lt;span class="n">styles&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">autoPrefixWXSS&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Compress&lt;/span> &lt;span class="n">WXML&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">minifyWXML&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Compress&lt;/span> &lt;span class="n">WXSS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">minifyWXSS&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">Compile&lt;/span> &lt;span class="n">JS&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">ES5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">codeProtect&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="bp">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="important-notes">
&lt;a class="heading-anchor-link" href="#important-notes">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official documentation lacks many details, so here are some clarifications:&lt;/p>
&lt;ol>
&lt;li>Note that if the version is already set as the preview version on the WeChat Official Accounts Platform, it will be published as the preview version; otherwise, it will be the development version.&lt;/li>
&lt;li>The private.key file needs to be downloaded from the WeChat Official Accounts Platform. Path: Development -&amp;gt; Development Settings -&amp;gt; Download Developer Tools Configuration File. After extracting, you can find the private.key file.&lt;/li>
&lt;li>The projectPath here is the directory where project.config exists. Since I&amp;rsquo;m using Taro, which handles compilation to WeChat Mini Program language, I need to disable those compilation switches in the configuration above. If you&amp;rsquo;re developing natively, I believe you can simply use the project configuration directly.&lt;/li>
&lt;li>The ci tool&amp;rsquo;s upload function only performs size checks and doesn&amp;rsquo;t validate syntax. So if you select the wrong directory or enable extra compilation options, the result is that the upload succeeds but the preview fails with errors like &lt;code>Component is not found in path &amp;quot;wx://not-found&amp;quot;&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;code>miniprogram-ci&lt;/code> isn&amp;rsquo;t about whether it&amp;rsquo;s good or not - it&amp;rsquo;s just barely usable.&lt;/li>
&lt;li>WeChat Mini Program documentation is truly terrible, and the community is mediocre at best. Just browse through and you&amp;rsquo;ll see complaints everywhere. What does this tell you? It&amp;rsquo;s bad, but you have no choice but to use it. Just make do with it.&lt;/li>
&lt;/ol></description></item><item><title>Claude Code with Exa MCP</title><link>https://en.1991421.cn/2026/01/14/claude-code-with-exa-mcp/</link><pubDate>Wed, 14 Jan 2026 18:24:18 +0800</pubDate><guid>https://en.1991421.cn/2026/01/14/claude-code-with-exa-mcp/</guid><description>&lt;blockquote>
&lt;p>Current AI tools already have web search capabilities. For example, Claude Code under CC integrates web search functionality. However, I heard Exa is a great AI search tool, so I tried it out for a day and found it quite good. Here&amp;rsquo;s my share.&lt;/p>
&lt;/blockquote>
&lt;h2 id="advantages-of-exa-search">
&lt;a class="heading-anchor-link" href="#advantages-of-exa-search">Advantages of Exa Search&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages-of-exa-search"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In my opinion, there are two main advantages: 1) Quality, 2) Speed. While AI&amp;rsquo;s web search essentially involves direct searching, Exa seems to have its own index database, resulting in much better search quality and faster response times.&lt;/p>
&lt;h2 id="freepaid-usage">
&lt;a class="heading-anchor-link" href="#freepaid-usage">Free/Paid Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="freepaid-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Exa currently offers a free tier - you get $15 USD free credit upon registration, which you can use for a while before needing to pay. Good thing it supports domestic Visa payment.
I&amp;rsquo;m still on the free tier and will consider paying once the free credit runs out.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/2026-01-14-184225.jpeg"
alt="https://static.1991421.cn/2026/2026-01-14-184225.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After registering, visit &lt;a href="https://dashboard.exa.ai/api-keys" target="_blank" rel="noopener">https://dashboard.exa.ai/api-keys&lt;/a> to get your API key.&lt;/p>
&lt;p>Run the following command in the terminal:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">claude mcp add exa -e &lt;span class="nv">EXA_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>YOUR_API_KEY -- npx -y exa-mcp-server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: After installing under CC, it&amp;rsquo;s recommended to restart CC and check &lt;code>/mcp&lt;/code> to see if it&amp;rsquo;s installed and running properly.&lt;/p>
&lt;p>Since I also use VSCode regularly, here&amp;rsquo;s how to install it in VSCode:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;exa&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;-y&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;exa-mcp-server&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;EXA_API_KEY&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;xxxxxx&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The simplest way is to directly type something like &lt;code>Use exa to search how to preview images in WeChat Mini Programs&lt;/code> when chatting with Claude. You&amp;rsquo;ll get relatively high-quality responses, and the response speed is indeed fast.&lt;/p>
&lt;h3 id="replacing-ais-built-in-web-search">
&lt;a class="heading-anchor-link" href="#replacing-ais-built-in-web-search">Replacing AI&amp;rsquo;s Built-in Web Search&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="replacing-ais-built-in-web-search"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>It&amp;rsquo;s annoying to explicitly specify exa in the prompt every time. You can modify your global &lt;code>claude.md&lt;/code> context and add the following constraints. This way, the AI will use the exa MCP instead of the built-in web search when it needs to call a search tool.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Tool Usage Policy
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">When&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">performing&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">any&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">retrieval&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">or&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">based&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">research&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">You&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MUST&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">exa&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MCP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">all&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">discovery&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Do&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Claude&lt;/span>&lt;span class="s1">&amp;#39;s built-in web search.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- Do NOT use any other MCPs for searching unless explicitly instructed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- If `exa` is unavailable, you must ask the user how to proceed instead of falling back to other tools.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">This rule applies to:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- Technical research
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- Documentation lookup
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- News or blog discovery
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- Any task that requires external information
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"># Tool Usage Policy
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">When performing any search, retrieval, or web-based research task:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- You MUST use the `exa` MCP for all search and content discovery.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- Do NOT use Claude&amp;#39;&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">built&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">web&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Do&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">any&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">other&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MCPs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">searching&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">unless&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">explicitly&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instructed&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">If&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">exa&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">unavailable&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">you&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">must&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ask&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">user&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">how&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">proceed&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">instead&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">of&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">falling&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">back&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">other&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">This&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rule&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">applies&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Technical&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">research&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Documentation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">lookup&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">News&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">or&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blog&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">discovery&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Any&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">requires&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">external&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Overall, the experience of using Exa with Claude Code is quite good, especially with the noticeable improvements in search quality and speed. I recommend everyone to give it a try.&lt;/li>
&lt;li>Currently, the only two MCPs I use frequently under CC are Figma and Exa; I haven&amp;rsquo;t used other MCPs much yet.&lt;/li>
&lt;/ol></description></item><item><title>How to Use Web Scraper to Crawl Website Data (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/12/18/web-scraper-crawl-website-data/</link><pubDate>Thu, 18 Dec 2025 13:55:16 +0800</pubDate><guid>https://en.1991421.cn/2025/12/18/web-scraper-crawl-website-data/</guid><description>&lt;blockquote>
&lt;p>While developing WeChat Mini Programs, I often find the official documentation difficult to navigate. To solve this, I decided to use Web Scraper to gather data from the official site and create a custom GPT or Claude Skill for querying. Despite being new to Web Scraper, I found it incredibly efficient once I got the hang of it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installing-the-web-scraper-extension">
&lt;a class="heading-anchor-link" href="#installing-the-web-scraper-extension">Installing the Web Scraper Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-the-web-scraper-extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Free vs. Paid&lt;/strong>: Web Scraper offers a official cloud-based crawling service for a fee. However, for personal use, the free browser extension is usually enough. The extension&amp;rsquo;s main limitation is that it primarily scrapes text data, but that’s exactly what I need for my AI projects.&lt;/li>
&lt;li>&lt;strong>Browser Support&lt;/strong>: The extension is officially available for Chrome. Other browsers may have limited or unofficial support.&lt;/li>
&lt;/ol>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I prefer configuring the scraper directly within the Chrome Developer Tools.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-18-140029.jpeg"
alt="Web Scraper Interface"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="exporting-data">
&lt;a class="heading-anchor-link" href="#exporting-data">Exporting Data&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="exporting-data"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The tool supports exporting to &lt;code>.xlsx&lt;/code> and &lt;code>.csv&lt;/code>. I typically use CSV because it’s a lightweight format that most AI models can easily ingest.&lt;/p>
&lt;h2 id="using-the-data-with-ai">
&lt;a class="heading-anchor-link" href="#using-the-data-with-ai">Using the Data with AI&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-the-data-with-ai"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Once you have the data, the possibilities are endless. For example, I created a &lt;strong>Custom GPT&lt;/strong> in ChatGPT and uploaded the scraped documentation. Now, I can query the official documentation through a conversational interface.&lt;/p>
&lt;p>I’ve built a &lt;strong>WeChat Mini Program Documentation Assistant&lt;/strong> using this method. You can try it out here:
&lt;a href="https://chatgpt.com/g/g-6940d6a0ef608191ae87c250ae4db438-wei-xin-xiao-cheng-xu-guan-fang-wen-dang-xiao-zhu-shou" target="_blank" rel="noopener">WeChat Mini Program Documentation Assistant GPT&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>While specialized APIs are often used for web scraping, many sites block automated crawlers. A browser-based extension like Web Scraper elegantly bypasses many of these restrictions by mimicking human browsing behavior. It’s a powerful tool for anyone looking to build custom datasets. Highly recommended!&lt;/p></description></item><item><title>Antigravity with Figma MCP Server</title><link>https://en.1991421.cn/2025/12/16/antigravity-figma-mcp-server/</link><pubDate>Tue, 16 Dec 2025 15:27:30 +0800</pubDate><guid>https://en.1991421.cn/2025/12/16/antigravity-figma-mcp-server/</guid><description>&lt;blockquote>
&lt;p>Recently, I tried Google&amp;rsquo;s Antigravity IDE with the G3 model and Figma MCP Server. The results were quite good, so I&amp;rsquo;m sharing how to use it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="antigravity-installation">
&lt;a class="heading-anchor-link" href="#antigravity-installation">Antigravity Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="antigravity-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>No need for lengthy explanations, just download and install from the official site. Google login is required. The free version of Antigravity can use g3. Regarding the quota, actual testing shows I can use it for 2 days. If that&amp;rsquo;s not enough, consider paying or switching accounts.&lt;/p>
&lt;h2 id="figma-mcp-server-installation">
&lt;a class="heading-anchor-link" href="#figma-mcp-server-installation">Figma MCP Server Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="figma-mcp-server-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Installing MCP in Antigravity is relatively convenient, but also somewhat hidden.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-16-153023.jpeg"
alt="https://static.1991421.cn/2025/2025-12-16-153023.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Search for and install the Figma MCP Server, then install it.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-16-153044.jpeg"
alt="https://static.1991421.cn/2025/2025-12-16-153044.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For actual usage, just paste the Figma design layer link in the prompt, and specify that the code should be generated according to the Figma design.&lt;/p>
&lt;h2 id="figma-mcp-settings">
&lt;a class="heading-anchor-link" href="#figma-mcp-settings">Figma MCP Settings&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="figma-mcp-settings"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>One small detail is that the Figma side can configure how images and icons are handled. This way, during use, the AI can download Figma resources to the project or directly use web addresses, etc. The official documentation is quite rough, so I&amp;rsquo;m specifically mentioning this here.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-16-164048.jpeg"
alt="https://static.1991421.cn/2025/2025-12-16-164048.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="usage-experience">
&lt;a class="heading-anchor-link" href="#usage-experience">Usage Experience&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-experience"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Currently, compared to Sonnet/GPT, I think this has the highest fidelity. Recommended to try out.&lt;/p></description></item><item><title>Creating Claude Skills with Claude Code</title><link>https://en.1991421.cn/2025/12/14/create-claude-skills-with-claude-code/</link><pubDate>Sun, 14 Dec 2025 18:13:15 +0800</pubDate><guid>https://en.1991421.cn/2025/12/14/create-claude-skills-with-claude-code/</guid><description>&lt;blockquote>
&lt;p>With the growing adoption of AI &amp;ldquo;Skills&amp;rdquo; as a standard for extending AI capabilities, I decided to create a specialized Skill for WeChat Mini Program documentation. This allows Claude to accurately reference official docs while assisting with development.&lt;/p>
&lt;/blockquote>
&lt;h2 id="scrapping-wechat-mini-program-documentation">
&lt;a class="heading-anchor-link" href="#scrapping-wechat-mini-program-documentation">Scrapping WeChat Mini Program Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scrapping-wechat-mini-program-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I used &lt;a href="https://webscraper.io/" target="_blank" rel="noopener">Web Scraper&lt;/a>, a free &lt;a href="https://chromewebstore.google.com/detail/web-scraper-free-web-scra/jnhgnonknehpejjnehehllkliplmbmhn?hl=en" target="_blank" rel="noopener">Chrome extension&lt;/a>, to gather the data.&lt;/p>
&lt;ol>
&lt;li>Configure the sitemap to navigate through the official documentation.&lt;/li>
&lt;li>Select the specific content elements (API descriptions, components, etc.) to scrape.&lt;/li>
&lt;li>Start the scraping process. The extension will open a new window to crawl the site. Due to the large volume of data, this may take some time.&lt;/li>
&lt;li>Once completed, export the data as a CSV file.&lt;/li>
&lt;/ol>
&lt;h2 id="creating-skills-in-claude-code">
&lt;a class="heading-anchor-link" href="#creating-skills-in-claude-code">Creating Skills in Claude Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="creating-skills-in-claude-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Claude Code provides built-in tools for creating Skills. When you create a Skill, it results in a &lt;code>.skill&lt;/code> file, which is essentially a ZIP archive. You can find these in the &lt;code>~/.claude/skills&lt;/code> directory.&lt;/p>
&lt;h3 id="adding-official-skills">
&lt;a class="heading-anchor-link" href="#adding-official-skills">Adding Official Skills&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-official-skills"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>You can reference and add official skills from the Anthropic repository:
&lt;a href="https://github.com/anthropics/skills.git" target="_blank" rel="noopener">https://github.com/anthropics/skills.git&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-16-183602.jpeg"
alt="Claude Skills List"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="building-the-wechat-mini-program-skill">
&lt;a class="heading-anchor-link" href="#building-the-wechat-mini-program-skill">Building the WeChat Mini Program Skill&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="building-the-wechat-mini-program-skill"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I used the scraped data to feed into a new Skill definition.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-16-183702.jpeg"
alt="Creating WeChat Skill"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Currently, Claude Code doesn&amp;rsquo;t have a UI to list installed Skills; you must use the command line to verify them.&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Once installed, you can simply ask questions related to the document in Claude Code:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;How do I get the user&amp;rsquo;s avatar in a WeChat Mini Program?&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Claude will automatically trigger the WeChat Mini Program documentation Skill, search the indexed content, and return accurate results. You will see a notification in the terminal when a Skill is being invoked.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>So far, the results are promising. However, I’ll need to monitor token usage over the long term to see how cost-effective this approach is. Done!&lt;/p></description></item><item><title>Custom Slash Commands in Codex</title><link>https://en.1991421.cn/2025/12/07/codex-custom-slash-commands/</link><pubDate>Sun, 07 Dec 2025 23:02:18 +0800</pubDate><guid>https://en.1991421.cn/2025/12/07/codex-custom-slash-commands/</guid><description>&lt;blockquote>
&lt;p>Custom slash commands in CC are really convenient. For example, I often perform specific operations directly with slash commands, which is efficient. The Codex CLI can achieve similar functionality.&lt;/p>
&lt;/blockquote>
&lt;h2 id="create-prompts-file">
&lt;a class="heading-anchor-link" href="#create-prompts-file">Create prompts file&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-prompts-file"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Create a &lt;code>prompts&lt;/code> folder in your user&amp;rsquo;s home directory, not in the project directory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">mkdir -p ~/.codex/prompts
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Edit the prompt file
Create a new text file in the &lt;code>~/.codex/prompts/&lt;/code> directory, such as &lt;code>deploy.md&lt;/code>, and write your desired prompt content.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="restart-codex-session">
&lt;a class="heading-anchor-link" href="#restart-codex-session">Restart Codex session&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="restart-codex-session"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Type &lt;code>/prompts:deploy-dev&lt;/code> and press enter to execute the target command.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-12-07-233812.jpeg"
alt="codex-custom-slash-command-1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developers.openai.com/codex/guides/slash-commands/#create-your-own-slash-commands-with-custom-prompts" target="_blank" rel="noopener">https://developers.openai.com/codex/guides/slash-commands/#create-your-own-slash-commands-with-custom-prompts&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Claude Pro Nigeria Low-Price Region Subscription Guide: Cheapest Way to Subscribe &amp; Payment Tips</title><link>https://en.1991421.cn/2025/11/29/claude-pro-low-price-region-subscription/</link><pubDate>Sat, 29 Nov 2025 16:09:50 +0800</pubDate><guid>https://en.1991421.cn/2025/11/29/claude-pro-low-price-region-subscription/</guid><description>&lt;blockquote>
&lt;p>While reviewing my annual app subscription costs recently, I found they had gotten way too high. One of the biggest expenses was Claude Pro at $20/month (~¥140 CNY), roughly ¥1,800/year. To cut costs, I switched to the Nigeria region subscription — now about &lt;strong>¥88 CNY/month&lt;/strong>, saving nearly half. Here&amp;rsquo;s how I did it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="why-choose-the-nigeria-region">
&lt;a class="heading-anchor-link" href="#why-choose-the-nigeria-region">Why Choose the Nigeria Region&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-choose-the-nigeria-region"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>I already use Turkey region for some services like YouTube Premium and GPT Plus. However, Claude&amp;rsquo;s Turkey pricing is barely cheaper than the US price, so there&amp;rsquo;s no real advantage. That&amp;rsquo;s why I went with Nigeria instead.&lt;/li>
&lt;li>If money isn&amp;rsquo;t a concern or your company covers it, there&amp;rsquo;s no need to bother — just go with the US region.&lt;/li>
&lt;li>As shown in the screenshot below, Nigeria is significantly cheaper than the US. For Max, the US price is $200 while Nigeria is $144.6 — a big difference. The gap is even more pronounced for the Pro tier.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/03/2026-03-25-223002.jpeg"
alt="Claude Pro Nigeria vs US pricing comparison"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="prerequisites">
&lt;a class="heading-anchor-link" href="#prerequisites">Prerequisites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prerequisites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>A Nigerian Apple ID account. I registered a new one. If creating a Nigerian account directly keeps failing, you can start with another region (e.g., China or the US) and switch the account country later in settings.
&lt;ul>
&lt;li>If you need help registering a Nigerian Apple ID or run into issues, check out &lt;a href="https://en.1991421.cn/2026/04/09/how-to-register-apple-id-in-nigeria-turkey/">this tutorial&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>A Nigerian payment method is ideal but not required. If you don&amp;rsquo;t have one, you can buy Nigerian iTunes gift cards instead. Chinese users can usually use Xianyu (Idle Fish); if you do not have access to a Chinese Xianyu account, use another gift-card channel.&lt;/li>
&lt;/ul>
&lt;!-- en-only:gift-card-service -->
&lt;div class="gcs-card">
&lt;div class="gcs-header">
&lt;div class="gcs-header-icon">🎁&lt;/div>
&lt;div>
&lt;div class="gcs-title">Nigerian iTunes Gift Card&lt;/div>
&lt;div class="gcs-subtitle">~14,900–15,000 NGN · Delivered within 24 hrs · Slightly above market price to cover service &amp; transaction fees&lt;/div>
&lt;/div>
&lt;div class="gcs-price-tag" id="gcs-price-tag">USD $--&lt;span>/card&lt;/span>&lt;/div>
&lt;/div>
&lt;div class="gcs-min-notice" id="gcs-min-notice">⚠️ Loading...&lt;/div>
&lt;div class="gcs-body">
&lt;div class="gcs-qty-row">
&lt;span class="gcs-label">Quantity&lt;/span>
&lt;div class="gcs-qty-ctrl">
&lt;button class="gcs-qty-btn cursor-pointer" id="gcs-minus">−&lt;/button>
&lt;span id="gcs-qty-display">1&lt;/span>
&lt;button class="gcs-qty-btn cursor-pointer" id="gcs-plus">+&lt;/button>
&lt;/div>
&lt;span class="gcs-total" id="gcs-total">Total: USD $--&lt;/span>
&lt;/div>
&lt;div class="gcs-email-row">
&lt;input
type="email"
id="gcs-email"
class="gcs-email-input"
placeholder="Your email (to receive the card code)"
/>
&lt;/div>
&lt;button id="gcs-buy-btn" class="gcs-buy-btn cursor-pointer">
Pay with Card
&lt;/button>
&lt;p id="gcs-msg" class="gcs-msg" style="display:none">&lt;/p>
&lt;p class="gcs-note">
After payment, the gift card code will be sent to your email within 24 hours.
&lt;/p>
&lt;details class="payment-faq">
&lt;summary>Payment issues?&lt;/summary>
&lt;div class="payment-faq-body">If you see 'payment blocked' or 'too risky', this is Stripe's automated fraud prevention — not a merchant decision. Common causes and fixes:&lt;br>&lt;br>&lt;strong>1. VPN / Proxy&lt;/strong>&lt;br>Turn off your VPN or switch to a node matching your card's issuing country. IP-to-card country mismatch often triggers the block.&lt;br>&lt;br>&lt;strong>2. Card not enabled for international payments&lt;/strong>&lt;br>Make sure your credit/debit card has international online transactions enabled — some banks disable this by default.&lt;br>&lt;br>&lt;strong>3. Try a different card&lt;/strong>&lt;br>Prefer Visa / Mastercard credit cards. Prepaid or virtual cards are more likely to be blocked.&lt;br>&lt;br>&lt;strong>4. Browser issues&lt;/strong>&lt;br>Try incognito/private mode, or a different browser. Some extensions can interfere with the payment flow.&lt;br>&lt;br>&lt;strong>5. Still not working?&lt;/strong>&lt;br>Contact the author via Alipay or PayPal to complete the payment.&lt;/div>
&lt;/details>
&lt;/div>
&lt;/div>
&lt;script>
(function () {
var qty = 0;
var min = 0;
var max = 0;
var pricing = [];
var configLoaded = false;
var btn = document.getElementById('gcs-buy-btn');
var msg = document.getElementById('gcs-msg');
btn.disabled = true;
function getUnitPrice(q, p) {
var tier = p.find(function (t) { return q >= t.minQty &amp;&amp; q &lt;= t.maxQty; });
return tier ? tier.price : p[p.length - 1].price;
}
function updateDisplay() {
var unitPrice = getUnitPrice(qty, pricing);
document.getElementById('gcs-qty-display').textContent = qty;
document.getElementById('gcs-total').textContent = 'Total: USD $' + (qty * unitPrice);
document.getElementById('gcs-price-tag').innerHTML = 'USD $' + unitPrice + '&lt;span>/card&lt;/span>';
document.getElementById('gcs-minus').disabled = qty &lt;= min;
document.getElementById('gcs-plus').disabled = qty >= max;
}
fetch('/api/nigeria-gift-card-config')
.then(function (r) { return r.json(); })
.then(function (cfg) {
pricing = cfg.pricing;
min = cfg.min;
max = cfg.max;
qty = cfg.defaultQty || cfg.min;
configLoaded = true;
document.getElementById('gcs-min-notice').textContent =
'💡 USD $16/card. Recommended: 2 cards.';
btn.disabled = false;
updateDisplay();
})
.catch(function () {
msg.style.display = 'block';
msg.className = 'gcs-msg gcs-msg-error';
msg.textContent = 'Failed to load pricing. Please refresh the page.';
document.getElementById('gcs-min-notice').textContent = '⚠️ Failed to load configuration.';
});
document.getElementById('gcs-minus').addEventListener('click', function () {
if (configLoaded &amp;&amp; qty > min) { qty--; updateDisplay(); }
});
document.getElementById('gcs-plus').addEventListener('click', function () {
if (configLoaded &amp;&amp; qty &lt; max) { qty++; updateDisplay(); }
});
btn.addEventListener('click', function () {
if (!configLoaded) return;
var email = document.getElementById('gcs-email').value.trim();
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
msg.style.display = 'block';
msg.className = 'gcs-msg gcs-msg-error';
msg.textContent = 'Please enter a valid email address.';
return;
}
btn.disabled = true;
btn.textContent = 'Redirecting...';
msg.style.display = 'none';
fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 'gift_card', quantity: qty, email: email, returnUrl: window.location.href }),
})
.then(function (r) { return r.json(); })
.then(function (data) {
if (data.url) {
window.location.href = data.url;
} else {
msg.style.display = 'block';
msg.className = 'gcs-msg gcs-msg-error';
msg.textContent = data.error || 'Something went wrong. Please try again.';
btn.disabled = false;
btn.textContent = 'Pay with Card';
}
})
.catch(function () {
msg.style.display = 'block';
msg.className = 'gcs-msg gcs-msg-error';
msg.textContent = 'Network error. Please try again.';
btn.disabled = false;
btn.textContent = 'Pay with Card';
});
});
})();
&lt;/script>
&lt;style>
.gcs-card {
margin: 1.5rem 0;
border-radius: 14px;
border: 1px solid #d1fae5;
background: #fff;
overflow: hidden;
box-shadow: 0 2px 12px rgba(5, 150, 105, 0.08);
}
:root.dark .gcs-card {
background: #1a2e25;
border-color: #064e3b;
}
.gcs-header {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem 1.25rem;
background: linear-gradient(135deg, #059669 0%, #0d9488 100%);
color: white;
}
.gcs-header-icon {
font-size: 1.75rem;
flex-shrink: 0;
}
.gcs-title {
font-size: 1rem;
font-weight: 700;
}
.gcs-subtitle {
font-size: 0.8rem;
opacity: 0.85;
margin-top: 2px;
}
.gcs-price-tag {
margin-left: auto;
font-size: 1.5rem;
font-weight: 800;
white-space: nowrap;
}
.gcs-price-tag span {
font-size: 0.85rem;
font-weight: 400;
opacity: 0.85;
}
.gcs-body {
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 0.875rem;
}
.gcs-qty-row {
display: flex;
align-items: center;
gap: 0.75rem;
}
.gcs-label {
font-size: 0.9rem;
color: #374151;
font-weight: 500;
}
:root.dark .gcs-label {
color: #d1d5db;
}
.gcs-qty-ctrl {
display: flex;
align-items: center;
gap: 0;
border: 1px solid #d1d5db;
border-radius: 8px;
overflow: hidden;
}
:root.dark .gcs-qty-ctrl {
border-color: #374151;
}
.gcs-qty-btn {
width: 32px;
height: 32px;
border: none;
background: #f9fafb;
color: #374151;
font-size: 1.1rem;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s;
}
:root.dark .gcs-qty-btn {
background: #374151;
color: #e5e7eb;
}
.gcs-qty-btn:hover:not(:disabled) {
background: #e5e7eb;
}
:root.dark .gcs-qty-btn:hover:not(:disabled) {
background: #4b5563;
}
.gcs-qty-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
#gcs-qty-display {
min-width: 32px;
text-align: center;
font-size: 0.95rem;
font-weight: 600;
color: #111827;
}
:root.dark #gcs-qty-display {
color: #f9fafb;
}
.gcs-total {
margin-left: auto;
font-size: 0.95rem;
font-weight: 700;
color: #059669;
}
.gcs-email-input {
width: 100%;
padding: 0.6rem 0.875rem;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 0.9rem;
color: #111827;
background: #fff;
box-sizing: border-box;
transition: border-color 0.2s;
outline: none;
}
:root.dark .gcs-email-input {
background: #1f2937;
border-color: #374151;
color: #f3f4f6;
}
.gcs-email-input:focus {
border-color: #059669;
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.12);
}
.gcs-buy-btn {
width: 100%;
padding: 0.7rem;
background: linear-gradient(135deg, #059669 0%, #0d9488 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 0.95rem;
font-weight: 600;
transition: opacity 0.2s, transform 0.15s;
}
.gcs-buy-btn:hover:not(:disabled) {
opacity: 0.92;
transform: translateY(-1px);
}
.gcs-buy-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.gcs-msg {
font-size: 0.85rem;
padding: 0.5rem 0.75rem;
border-radius: 6px;
margin: 0;
}
.gcs-msg-error {
background: #fef2f2;
color: #dc2626;
}
:root.dark .gcs-msg-error {
background: #3f1515;
color: #f87171;
}
.gcs-note {
font-size: 0.8rem;
color: #9ca3af;
margin: 0;
text-align: center;
}
.gcs-note a {
color: #059669;
}
.gcs-min-notice {
padding: 0.5rem 1.25rem;
font-size: 0.8rem;
color: #92400e;
background: #fef3c7;
border-top: 1px solid #fde68a;
}
:root.dark .gcs-min-notice {
color: #fcd34d;
background: #422006;
border-color: #713f12;
}
&lt;/style>
&lt;!-- /en-only:gift-card-service -->
&lt;h2 id="subscription-steps">
&lt;a class="heading-anchor-link" href="#subscription-steps">Subscription Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="subscription-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Open the App Store and sign in with your Nigerian Apple ID.&lt;/li>
&lt;li>Tap your profile icon in the top right, select &lt;strong>Add Funds&lt;/strong>, then choose &lt;strong>Redeem Gift Card or Code&lt;/strong> and enter the gift card code you purchased.&lt;/li>
&lt;li>Open the Claude app, sign in to your account, and go to the subscription page.&lt;/li>
&lt;li>Select the monthly plan and complete the payment as prompted.&lt;/li>
&lt;li>If you only want one month, remember to &lt;strong>cancel the auto-renewal&lt;/strong> after subscribing.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-11-29-161858.jpeg"
alt="Claude Pro subscription screen"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Note: To subscribe via a different region, you must sign into the Nigerian Apple ID on an iPhone. iPad should theoretically work as well. For Android or other devices, please check on your own.&lt;/strong>&lt;/p>
&lt;h2 id="subscription-failed--cannot-subscribe">
&lt;a class="heading-anchor-link" href="#subscription-failed--cannot-subscribe">Subscription Failed / Cannot Subscribe&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="subscription-failed--cannot-subscribe"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>For a newly registered account or an account that has just switched regions, Apple may apply purchase protection. In the early stage, this can cause paid subscriptions for some apps to fail. This is a normal Apple risk-control mechanism, not a gift card issue. Usually, waiting a few days will resolve it automatically.&lt;/li>
&lt;li>I ran into similar issues before when subscribing to services like YouTube Premium. You can try waiting a few days.&lt;/li>
&lt;/ol>
&lt;h2 id="final-notes">
&lt;a class="heading-anchor-link" href="#final-notes">Final Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Claude Pro subscribed through a low-price region has exactly the same features as the US or any other region — no differences at all.&lt;/li>
&lt;li>Having a Nigerian and Turkish Apple ID on hand is always useful. I recommend setting them up in advance.&lt;/li>
&lt;li>For a full list of services I&amp;rsquo;m currently subscribed to, check out &lt;a href="https://en.1991421.cn/2025/11/16/my-subscribed-services-list/" target="_blank" rel="noopener">My App Subscription List&lt;/a>.&lt;/li>
&lt;/ul></description></item><item><title>How to Use the Gemini CLI (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/11/27/how-to-use-gemini-cli/</link><pubDate>Thu, 27 Nov 2025 22:05:13 +0800</pubDate><guid>https://en.1991421.cn/2025/11/27/how-to-use-gemini-cli/</guid><description>&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You can install the Gemini CLI using Homebrew:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install gemini-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Alternatively, you can use npm:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install -g @google/gemini-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="upgrading">
&lt;a class="heading-anchor-link" href="#upgrading">Upgrading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To update to the latest version via Homebrew:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew upgrade gemini-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="login">
&lt;a class="heading-anchor-link" href="#login">Login&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="login"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Once you enter a Gemini session, use the &lt;code>/login&lt;/code> command to authenticate. Since Gemini currently offers a free tier (e.g., G3), you can simply use your Google account to log in.&lt;/p>
&lt;h2 id="installing-the-figma-mcp-extension">
&lt;a class="heading-anchor-link" href="#installing-the-figma-mcp-extension">Installing the Figma MCP Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-the-figma-mcp-extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You can extend Gemini&amp;rsquo;s capabilities by installing the official Figma MCP (Model Context Protocol) server:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gemini extensions install https://github.com/figma/figma-gemini-cli-extension
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To uninstall the extension:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gemini extensions uninstall figma
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/figma/figma-gemini-cli-extension" target="_blank" rel="noopener">Figma Gemini CLI Extension on GitHub&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/" target="_blank" rel="noopener">Official Figma MCP Server Documentation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>CodeTracker: A WakaTime Client for WeChat Mini Programs</title><link>https://en.1991421.cn/2025/11/23/wakatime-mini-program/</link><pubDate>Sun, 23 Nov 2025 12:03:49 +0800</pubDate><guid>https://en.1991421.cn/2025/11/23/wakatime-mini-program/</guid><description>&lt;blockquote>
&lt;p>For those of us who use WakaTime to track our coding hours, checking stats on mobile can be a bit clunky. Years ago, I started building a WeChat Mini Program for this, but I eventually abandoned it because the UI was lackluster—I didn&amp;rsquo;t even like using it myself. However, with the help of modern AI tools, I decided to reboot the project. After a few hours of AI-assisted coding, version 2.0 is finally out. It looks much better now, and I plan to keep iterating based on real-world needs.&lt;/p>
&lt;/blockquote>
&lt;h2 id="important-notes">
&lt;a class="heading-anchor-link" href="#important-notes">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>This app acts solely as a client for WakaTime. All data is fetched from the official WakaTime API. To use it, you must already have a WakaTime account and the corresponding plugins installed in your IDE (VS Code, JetBrains, etc.).&lt;/li>
&lt;li>To avoid potential trademark issues, I’ve named the app &lt;strong>CodeTracker&lt;/strong> instead of &amp;ldquo;WakaTime Mini Program.&amp;rdquo; This also gives me more flexibility to add non-WakaTime features in the future.&lt;/li>
&lt;/ul>
&lt;h2 id="the-app">
&lt;a class="heading-anchor-link" href="#the-app">The App&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-app"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you&amp;rsquo;re interested in tracking your coding productivity on the go, feel free to give it a try!&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-11-23-120617.jpeg"
alt="CodeTracker Mini Program Preview"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="roadmap">
&lt;a class="heading-anchor-link" href="#roadmap">Roadmap&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="roadmap"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that I’m using it daily, I’ve already thought of a few new ideas:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Daily Reports&lt;/strong>: A scheduled push notification (e.g., at 7:00 PM) summarizing today&amp;rsquo;s coding activity.&lt;/li>
&lt;li>&lt;strong>Visual Avatars&lt;/strong>: A virtual character that reflects your coding intensity—for example, a character developing &amp;ldquo;dark circles&amp;rdquo; under their eyes if you&amp;rsquo;ve been coding for too long.&lt;/li>
&lt;li>&lt;strong>Goal Tracking&lt;/strong>: Setting weekly coding targets and tracking progress.&lt;/li>
&lt;/ul>
&lt;p>For now, I’m focusing on perfecting the core functionality. Stay tuned for more updates!&lt;/p></description></item><item><title>Implementing Email Notifications for Waline Comments</title><link>https://en.1991421.cn/2025/11/16/waline-email-notification/</link><pubDate>Sun, 16 Nov 2025 20:15:11 +0800</pubDate><guid>https://en.1991421.cn/2025/11/16/waline-email-notification/</guid><description>&lt;blockquote>
&lt;p>I integrated the Waline comment system into my blog a while ago. Recently, I noticed that several readers had left questions on my posts, but because I hadn&amp;rsquo;t set up notifications, I didn&amp;rsquo;t see them in time to respond. Since Waline supports various notification methods, I decided to configure email alerts. Here is the process for future reference.&lt;/p>
&lt;/blockquote>
&lt;h2 id="choosing-a-notification-method">
&lt;a class="heading-anchor-link" href="#choosing-a-notification-method">Choosing a Notification Method&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="choosing-a-notification-method"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>WeChat was ruled out because it lacks an official bot API (current solutions are often &amp;ldquo;hacks&amp;rdquo;). While Telegram is an option, it can get quite noisy. For a blog, email is a professional and non-intrusive way to stay updated.&lt;/p>
&lt;h2 id="selecting-an-email-provider">
&lt;a class="heading-anchor-link" href="#selecting-an-email-provider">Selecting an Email Provider&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="selecting-an-email-provider"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I decided to use my QQ Mail account. Since it&amp;rsquo;s only acting as the sender, any reliable provider with a low chance of being flagged as spam will work.&lt;/p>
&lt;p>For QQ Mail, you need to generate an &lt;strong>Authorization Code&lt;/strong> from the settings to use as the SMTP password. &lt;em>Note: This is different from your regular login password.&lt;/em>&lt;/p>
&lt;p>&lt;strong>Pro-tip:&lt;/strong> The same authorization code can be used across multiple clients; for example, I use one code for two separate Waline server instances.&lt;/p>
&lt;h2 id="waline-server-configuration">
&lt;a class="heading-anchor-link" href="#waline-server-configuration">Waline Server Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="waline-server-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>My Waline server is hosted on Vercel, so I simply added the necessary environment variables to the project settings. After saving, restart the service to apply the changes.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Environment Variable&lt;/th>
&lt;th style="text-align: left">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;code>SMTP_SECURE&lt;/code>&lt;/td>
&lt;td style="text-align: left">Set to &lt;code>true&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>SMTP_USER&lt;/code>&lt;/td>
&lt;td style="text-align: left">Your email address (e.g., &lt;code>user@qq.com&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>SMTP_PASS&lt;/code>&lt;/td>
&lt;td style="text-align: left">Your SMTP Authorization Code&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>SMTP_SERVICE&lt;/code>&lt;/td>
&lt;td style="text-align: left">Your provider (e.g., &lt;code>QQ&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>SITE_URL&lt;/code>&lt;/td>
&lt;td style="text-align: left">The URL of your blog&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>SITE_NAME&lt;/code>&lt;/td>
&lt;td style="text-align: left">The name of your blog&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For more advanced options, refer to the &lt;a href="https://waline.js.org/guide/features/notification.html" target="_blank" rel="noopener">official Waline notification guide&lt;/a>.&lt;/p>
&lt;h2 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Once configured, you will receive an email like the one below whenever a new comment is posted:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-11-16-202035.jpeg"
alt="Waline Email Notification Example"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The email notification feature in Waline is extremely useful for bloggers who want to stay engaged with their readers. I hope future updates bring even more customization options.&lt;/li>
&lt;li>For lightweight blogs, Waline remains an excellent, easy-to-configure choice for a commenting system. Kudos to the open-source contributors!&lt;/li>
&lt;/ol>
&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://waline.js.org/guide/features/notification.html" target="_blank" rel="noopener">Waline Documentation: Comment Notifications&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Custom Slash Commands in Claude Code</title><link>https://en.1991421.cn/2025/11/11/claude-code-custom-slash-commands/</link><pubDate>Tue, 11 Nov 2025 23:01:33 +0800</pubDate><guid>https://en.1991421.cn/2025/11/11/claude-code-custom-slash-commands/</guid><description>&lt;blockquote>
&lt;p>In my current project, I frequently need to deploy builds to a development server. While CI/CD pipelines are great, they can be slow for quick iterations; sometimes a simple &lt;code>scp&lt;/code> command is much faster. Since I spend most of my time in the Claude Code (cc) CLI, I wanted a more integrated way to trigger these scripts without manually typing out bash commands. Here is how I set up custom slash commands in Claude Code.&lt;/p>
&lt;/blockquote>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Custom commands are scoped to the project. Create a directory named &lt;code>.claude/commands/&lt;/code> in your project root. For example, to create a development deployment command, create a file named &lt;code>deploy-dev.md&lt;/code>.&lt;/p>
&lt;p>The content should follow this structure: use Markdown for the description and a code block for the actual script.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">&lt;span class="gh"># Deploy to Development Server
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gh">&lt;/span>Run this command to build the project and sync files to the dev environment.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">```bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">scp -r ./dist user@dev-machine:/path/to/deploy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">After&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">saving&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restart&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">your&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">claude&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">You&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">can&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">execute&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">it&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">typing&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`/&lt;/span>&lt;span class="n">deploy&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">dev&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CLI&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">## Permissions
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Commands&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">like&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">scp&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">are&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">considered&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">sensitive&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">By&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">default&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Claude&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Code&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">will&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ask&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">permission&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">before&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">executing&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shell&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">scripts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">You&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">have&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">two&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">options&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Start&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Claude&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Code&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">with&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`--&lt;/span>&lt;span class="n">dangerously&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">skip&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">permissions&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">flag&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">recommended&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">general&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Allow&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">command&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">prompted&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Always allow for this project&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">option&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">add&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">it&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">your&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="s1">&amp;#39;s whitelist.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">## Summary
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">1. This is a basic example, but you can use natural language within the Markdown file to provide more context or complex instructions for the command.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2. The Claude Code CLI is arguably the best AI-driven terminal tool available right now. Mastering its custom commands can significantly boost your development workflow.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Guide to Using Claude Code with Codex</title><link>https://en.1991421.cn/2025/11/09/claude-codex-guide/</link><pubDate>Sun, 09 Nov 2025 22:57:38 +0800</pubDate><guid>https://en.1991421.cn/2025/11/09/claude-codex-guide/</guid><description>&lt;blockquote>
&lt;p>I primarily use Claude Code (cc) for development, but the Pro membership&amp;rsquo;s quota is surprisingly limited. I frequently hit the ceiling and have to wait for my weekly or hourly reset. To keep my momentum, I needed a reliable backup. While DeepSeek is cheap, it doesn&amp;rsquo;t quite match the quality I need. I decided to try Codex, and it has exceeded my expectations. Since they are products from different companies, there are some nuances to using them together. Here’s a summary of my experience.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>(Note: This post is continuously updated.)&lt;/em>&lt;/p>
&lt;h2 id="requirements">
&lt;a class="heading-anchor-link" href="#requirements">Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Codex&lt;/strong>: Requires a ChatGPT Plus (or higher) subscription; it is not available for free users.&lt;/li>
&lt;li>&lt;strong>Claude Code&lt;/strong>: Requires a Pro or Max membership, or a pay-as-you-go API account.&lt;/li>
&lt;/ul>
&lt;h2 id="project-configuration-files">
&lt;a class="heading-anchor-link" href="#project-configuration-files">Project Configuration Files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-configuration-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Codex uses the &lt;code>/init&lt;/code> command to generate an &lt;code>AGENTS.md&lt;/code> file, while Claude Code uses &lt;code>/init&lt;/code> to generate a &lt;code>CLAUDE.md&lt;/code> file. Maintaining both manually is tedious.&lt;/p>
&lt;p>My solution is to link them: in &lt;code>AGENTS.md&lt;/code>, I added the instruction: &lt;code>Read CLAUDE.md for more details on development commands, project architecture, and key directories.&lt;/code> My testing shows that Codex will then automatically read the contents of &lt;code>CLAUDE.md&lt;/code>. This allows me to maintain only the &lt;code>CLAUDE.md&lt;/code> file for project-specific rules. You can also do the reverse if you prefer &lt;code>AGENTS.md&lt;/code>.&lt;/p>
&lt;h2 id="custom-commands">
&lt;a class="heading-anchor-link" href="#custom-commands">Custom Commands&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-commands"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Custom slash commands are implemented differently in Claude Code and Codex. Unfortunately, these cannot be shared directly and must be configured separately for each CLI. If you don&amp;rsquo;t use custom commands, you can ignore this part.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>CLI Experience&lt;/strong>: In terms of pure terminal user experience, Claude Code (cc) is the clear winner. For example, &lt;code>cc&lt;/code> handles session recovery gracefully across logins. Codex requires you to manually select a session upon startup, and it doesn&amp;rsquo;t effectively isolate sessions by repository, which can lead to a cluttered session list.&lt;/li>
&lt;li>&lt;strong>Code Quality&lt;/strong>: Both have their strengths. Codex noticeably outperforms Claude Code when generating code from Figma via the MCP server. However, for general logic and iterative coding, I still find Claude&amp;rsquo;s output to be slightly superior.&lt;/li>
&lt;li>&lt;strong>Cost vs. Quota&lt;/strong>: Given the quota issues, I&amp;rsquo;ve opted for a dual-subscription strategy: &lt;strong>GPT Plus + Claude Pro&lt;/strong>. Since both memberships also provide access to their respective web interfaces, I find the value proposition to be solid. If you&amp;rsquo;re constantly hitting your Claude quota, this hybrid approach is a great way to stay productive.&lt;/li>
&lt;/ol></description></item><item><title>Integrating Codex with Figma MCP Server</title><link>https://en.1991421.cn/2025/11/09/codex-figma-mcp/</link><pubDate>Sun, 09 Nov 2025 22:13:30 +0800</pubDate><guid>https://en.1991421.cn/2025/11/09/codex-figma-mcp/</guid><description>&lt;blockquote>
&lt;p>Claude Code (cc) works well with Figma MCP, but the biggest hurdle is the limited weekly quota. Since I also have a ChatGPT Plus subscription, I decided to see if I could bridge Codex with Figma MCP. Although there isn&amp;rsquo;t much official documentation on this, it is indeed possible and works quite well. Here is the setup guide.&lt;/p>
&lt;/blockquote>
&lt;h2 id="requirements">
&lt;a class="heading-anchor-link" href="#requirements">Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Figma Permissions&lt;/strong>: You need &lt;code>Dev&lt;/code> or full &lt;code>Editor&lt;/code> permissions.&lt;/li>
&lt;li>&lt;strong>ChatGPT Plus Membership&lt;/strong>: Codex is currently not available for free users; I use the GPT Plus tier.&lt;/li>
&lt;/ol>
&lt;h2 id="codex-configuration">
&lt;a class="heading-anchor-link" href="#codex-configuration">Codex Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="codex-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Locate your configuration file at &lt;code>~/.codex/config.toml&lt;/code> and add the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">experimental_use_rmcp_client&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">mcp_servers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">figma_local&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">url&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="s2">&amp;#34;http://127.0.0.1:3845/mcp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this setup, I am using a locally hosted MCP server, so make sure you have the Figma MCP server running locally.&lt;/p>
&lt;p>Once configured, start Codex. If there’s a connection issue with the MCP server, you’ll see an error; otherwise, you&amp;rsquo;re good to go.&lt;/p>
&lt;p>&lt;em>Note: &lt;code>experimental_use_rmcp_client = true&lt;/code> is essential for enabling HTTP stream support, which is required to interface with the Figma MCP server.&lt;/em>&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Copy Layer Link&lt;/strong>: In Figma, right-click the desired layer and copy the link.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-11-09-222206.jpeg"
alt="Figma Copy Link"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>&lt;strong>Command Codex&lt;/strong>: Use natural language in Codex to ask for code generation or modifications based on the linked design.&lt;/li>
&lt;/ol>
&lt;h2 id="personal-impressions">
&lt;a class="heading-anchor-link" href="#personal-impressions">Personal Impressions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="personal-impressions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Fidelity&lt;/strong>: In my experience, Codex sometimes offers slightly higher fidelity when translating Figma designs into code compared to Claude. However, for general logic and complex coding tasks, Claude remains superior. Note that neither is perfect—you will still need to manually polish the generated code, but it definitely saves a significant amount of time.&lt;/li>
&lt;li>&lt;strong>Cost &amp;amp; Quota&lt;/strong>: A single Claude Pro subscription&amp;rsquo;s weekly quota is often insufficient for heavy use. If you find yourself hitting the limit, combining GPT Plus with Claude Pro (totaling around &lt;strong>250 CNY/month&lt;/strong>) provides a robust, multi-model workflow that keeps you productive.&lt;/li>
&lt;/ol></description></item><item><title>How to Use Claude Code with the Figma MCP Server (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/11/01/claudecode-figma-mcp/</link><pubDate>Sat, 01 Nov 2025 23:46:44 +0800</pubDate><guid>https://en.1991421.cn/2025/11/01/claudecode-figma-mcp/</guid><description>&lt;blockquote>
&lt;p>My project&amp;rsquo;s UI designs are provided as Figma files. Since Figma recently released an official MCP (Model Context Protocol) server, I decided to experiment with combining Claude Code and Figma MCP to generate and modify code based directly on design specs.&lt;/p>
&lt;/blockquote>
&lt;h2 id="requirements">
&lt;a class="heading-anchor-link" href="#requirements">Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Figma Permissions&lt;/strong>: You must have &lt;code>Dev&lt;/code> mode access or full &lt;code>Editor&lt;/code> permissions.&lt;/li>
&lt;li>&lt;strong>Claude Code Membership or API Key&lt;/strong>: Required for using the official Claude Code CLI. (If you use other MCP-compatible clients, you might only need an API key).&lt;/li>
&lt;/ol>
&lt;h2 id="claude-code-configuration">
&lt;a class="heading-anchor-link" href="#claude-code-configuration">Claude Code Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="claude-code-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I used the local server installation for this setup. Once your local Figma MCP server is running, add it to Claude Code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">claude mcp add --transport sse figma-dev-mode-mcp-server http://127.0.0.1:3845/sse
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Verify the setup&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">claude mcp list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="usage-workflow">
&lt;a class="heading-anchor-link" href="#usage-workflow">Usage Workflow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-workflow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Select a Layer&lt;/strong>: Open your design in Figma and select the specific layer or component you want to work on.&lt;/li>
&lt;li>&lt;strong>Copy the Link&lt;/strong>: Right-click the node and select &amp;ldquo;Copy Link&amp;rdquo; (or use the &lt;code>Cmd + L&lt;/code> shortcut on macOS).&lt;/li>
&lt;li>&lt;strong>Instruct Claude&lt;/strong>: Paste the link into the Claude Code terminal and use natural language to tell the AI what you need (e.g., &amp;ldquo;Generate a React component based on this design link&amp;rdquo;).&lt;/li>
&lt;/ol>
&lt;h2 id="personal-impressions">
&lt;a class="heading-anchor-link" href="#personal-impressions">Personal Impressions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="personal-impressions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Fidelity&lt;/strong>: The results are surprisingly good and save a lot of boilerplate coding time. However, it&amp;rsquo;s not a &amp;ldquo;100% perfect&amp;rdquo; translation yet. There are often minor CSS or layout discrepancies that still require manual adjustment and polishing.&lt;/li>
&lt;/ol>
&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.builder.io/blog/claude-code-figma-mcp-server" target="_blank" rel="noopener">Builder.io Guide: Claude Code + Figma MCP&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.figma.com/docs/figma-mcp-server/local-server-installation/" target="_blank" rel="noopener">Official Figma MCP Server: Local Installation Guide&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Generating gRPC Web and gRPC SDK from Protocol Buffers</title><link>https://en.1991421.cn/2025/10/10/pb-generate-grpc-web-and-grpc-sdk/</link><pubDate>Fri, 10 Oct 2025 17:40:07 +0800</pubDate><guid>https://en.1991421.cn/2025/10/10/pb-generate-grpc-web-and-grpc-sdk/</guid><description>&lt;blockquote>
&lt;p>Recently working on web3js, I needed to revisit gRPC and organize the related gRPC-web and gRPC topics.&lt;/p>
&lt;/blockquote>
&lt;p>First, the code generated for gRPC and gRPC-web from Protocol Buffers is different. Therefore, different plugins must be used to generate the code.&lt;/p>
&lt;h2 id="generating-grpc-web-from-protocol-buffers">
&lt;a class="heading-anchor-link" href="#generating-grpc-web-from-protocol-buffers">Generating gRPC-web from Protocol Buffers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="generating-grpc-web-from-protocol-buffers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The generated code is in CommonJS format.&lt;/li>
&lt;li>The generated code can be JavaScript with TypeScript definition files (dts).&lt;/li>
&lt;li>The mode is set according to needs; both modes are selectable and relate to backend proxy gateways.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> protoc --proto_path&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PROTO_PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --js_out&lt;span class="o">=&lt;/span>&lt;span class="nv">import_style&lt;/span>&lt;span class="o">=&lt;/span>commonjs,binary:&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$OUTPUT_DIR&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --grpc-web_out&lt;span class="o">=&lt;/span>&lt;span class="nv">import_style&lt;/span>&lt;span class="o">=&lt;/span>commonjs+dts,mode&lt;span class="o">=&lt;/span>grpcwebtext:&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$OUTPUT_DIR&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$relative_dir&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="nv">$proto_filename&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -I &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PROTO_PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="generating-grpc-from-protocol-buffers">
&lt;a class="heading-anchor-link" href="#generating-grpc-from-protocol-buffers">Generating gRPC from Protocol Buffers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="generating-grpc-from-protocol-buffers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Requires installing the grpc_tools_node_protoc_ts package to support TypeScript definition generation.&lt;/li>
&lt;li>Requires installing grpc-tools, which provides the grpc_tools_node_protoc tool.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Using grpc_tools_node_protoc to generate JS + TS in one step&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">grpc_tools_node_protoc&lt;/span> \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">--&lt;/span>&lt;span class="n">plugin&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">protoc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ts&lt;/span>&lt;span class="o">=../../&lt;/span>&lt;span class="n">node_modules&lt;/span>&lt;span class="o">/.&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">protoc&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">gen&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">ts&lt;/span> \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&lt;/span>&lt;span class="n">I&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;$PROTO_PATH&amp;#34;&lt;/span> \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">--&lt;/span>&lt;span class="n">js_out&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">IMPORT_STYLE&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">OUTPUT_DIR&lt;/span>&lt;span class="p">}&lt;/span> \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">--&lt;/span>&lt;span class="n">grpc_out&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">grpc_js&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">OUTPUT_DIR&lt;/span>&lt;span class="p">}&lt;/span> \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">--&lt;/span>&lt;span class="n">ts_out&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">grpc_js&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">OUTPUT_DIR&lt;/span>&lt;span class="p">}&lt;/span> \
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;$grpc_proto&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="third-party-dependencies-in-protocol-buffer-files">
&lt;a class="heading-anchor-link" href="#third-party-dependencies-in-protocol-buffer-files">Third-party Dependencies in Protocol Buffer Files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="third-party-dependencies-in-protocol-buffer-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, &lt;code>import &amp;quot;google/api/http.proto&amp;quot;&lt;/code> requires downloading Google&amp;rsquo;s proto files and placing them in the specified directory to avoid errors during generation.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In summary, this covers generating gRPC client code for both web JavaScript and Node.js.&lt;/p></description></item><item><title>How to Use Chrome DevTools MCP in the Claude Desktop App (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/09/29/claude-desktop-app-chrome-devtools-mcp/</link><pubDate>Mon, 29 Sep 2025 14:46:00 +0800</pubDate><guid>https://en.1991421.cn/2025/09/29/claude-desktop-app-chrome-devtools-mcp/</guid><description>&lt;blockquote>
&lt;p>Google recently launched a Chrome DevTools MCP (Model Context Protocol) server, which essentially gives AI models the ability to control parts of the Chrome browser. As someone who frequently translates articles and manages content, my workflow often involves translating text, opening a site, and pasting content. I hoped MCP could automate this. During the setup, I encountered some tricky configuration issues—here’s how I solved them.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installing-chrome-devtools-mcp">
&lt;a class="heading-anchor-link" href="#installing-chrome-devtools-mcp">Installing Chrome DevTools MCP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-chrome-devtools-mcp"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In theory, you only need to add the following to your Claude Desktop configuration file (found at &lt;code>~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code> on macOS). However, note that MCP servers rely on Node.js, and this specific server requires &lt;strong>Node 22 or higher&lt;/strong>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;chrome-devtools&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;chrome-devtools-mcp@latest&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note: If the app doesn&amp;rsquo;t recognize the changes after you save the config file, you may need to restart the Claude Desktop app.&lt;/em>&lt;/p>
&lt;h2 id="solving-nodejs-version-conflicts-with-nvm">
&lt;a class="heading-anchor-link" href="#solving-nodejs-version-conflicts-with-nvm">Solving Node.js Version Conflicts with NVM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solving-nodejs-version-conflicts-with-nvm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you use NVM (Node Version Manager) on your Mac, you might run into trouble. On my machine, I have multiple versions ranging from v16 to v22. Since &lt;code>chrome-devtools-mcp&lt;/code> strictly requires Node 22+, and Claude might default to an older version (like my v16), it will fail to start.&lt;/p>
&lt;p>To fix this, you have two choices:&lt;/p>
&lt;ol>
&lt;li>Uninstall all Node versions below 22 (not ideal for developers).&lt;/li>
&lt;li>Explicitly point Claude to the Node 22 binary and the global module path.&lt;/li>
&lt;/ol>
&lt;p>I chose the second option. First, install the package globally:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Use Node 22 to install&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nvm use &lt;span class="m">22&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -g chrome-devtools-mcp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then, update your &lt;code>claude_desktop_config.json&lt;/code> with absolute paths:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mcpServers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;chrome-devtools&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/Users/alanhe/.nvm/versions/node/v22.15.1/bin/node&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;/Users/alanhe/.nvm/versions/node/v22.15.1/lib/node_modules/chrome-devtools-mcp/build/src/index.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This resolved the connection issues for me.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-09-29-145922.jpeg"
alt="Claude Desktop with Chrome DevTools MCP Active"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="using-mcp-within-claude-projects">
&lt;a class="heading-anchor-link" href="#using-mcp-within-claude-projects">Using MCP within Claude Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-mcp-within-claude-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I often use MCP within specialized Claude Projects. When writing your Project Instructions, be very explicit. Instruct the AI to follow strict sequential steps (e.g., &amp;ldquo;Step 1: Open the URL&amp;hellip; Step 2: Extract content&amp;hellip;&amp;rdquo;). This ensures the MCP triggers reliably; otherwise, the AI might skip the automation step.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Practicality&lt;/strong>: While the functionality is impressive, it has limitations. For instance, when asking the AI to type content into a web input field, it currently simulates keypresses one by one (like a typewriter effect), which is agonizingly slow. For large amounts of text, manual pasting is still faster.&lt;/li>
&lt;li>&lt;strong>Experimentation&lt;/strong>: Despite the speed issue, it&amp;rsquo;s a fascinating glimpse into the future of AI-browser interaction. It&amp;rsquo;s worth trying out just to see what&amp;rsquo;s possible.&lt;/li>
&lt;/ol>
&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/ChromeDevTools/chrome-devtools-mcp/" target="_blank" rel="noopener">Official Chrome DevTools MCP Repository&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/modelcontextprotocol/servers/issues/64" target="_blank" rel="noopener">Model Context Protocol Servers Discussion&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Investigation into Web Clipboard Copying Failures</title><link>https://en.1991421.cn/2025/09/25/clipboard-copy-issue/</link><pubDate>Thu, 25 Sep 2025 15:07:08 +0800</pubDate><guid>https://en.1991421.cn/2025/09/25/clipboard-copy-issue/</guid><description>&lt;blockquote>
&lt;p>Recently, I worked on a project using Tencent&amp;rsquo;s TEA component library. It includes a &lt;code>Copy&lt;/code> component that relies on &lt;code>copyToClipboard&lt;/code>, which in turn depends on &lt;code>document.execCommand('copy')&lt;/code>. This API has several pitfalls; here is a summary of what I encountered.&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-problem">
&lt;a class="heading-anchor-link" href="#the-problem">The Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When visiting a site via an IP-based URL, the &lt;code>Copy&lt;/code> component fails to copy text if a specific DOM element is currently in full-screen mode.&lt;/p>
&lt;h2 id="component-dependency-graph">
&lt;a class="heading-anchor-link" href="#component-dependency-graph">Component Dependency Graph&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-dependency-graph"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-09-30-165252.jpeg"
alt="Clipboard Copy Issue Dependencies"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="how-the-copy-component-works">
&lt;a class="heading-anchor-link" href="#how-the-copy-component-works">How the Copy Component Works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-the-copy-component-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Under the hood, it creates an invisible element, populates it with text, selects that text, executes &lt;code>document.execCommand('copy')&lt;/code>, and then removes the element from the DOM.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Deprecated API&lt;/strong>: &lt;code>document.execCommand('copy')&lt;/code> is deprecated. However, it is still widely used because it can function even when the browser hasn&amp;rsquo;t explicitly granted Clipboard API permissions.&lt;/li>
&lt;li>&lt;strong>Full-screen Conflict&lt;/strong>: In Chrome, &lt;code>document.execCommand('copy')&lt;/code> fails silently (no error, but no copy) when a DOM element is in full-screen mode.&lt;/li>
&lt;li>&lt;strong>Secure Context Requirement&lt;/strong>: The newer &lt;code>Clipboard API&lt;/code> requires a secure context (HTTPS). On internal sites or IP-based URLs that lack HTTPS, the Clipboard API will not work.&lt;/li>
&lt;/ol>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Prefer the Clipboard API&lt;/strong>: Always attempt to use the modern &lt;code>navigator.clipboard&lt;/code> API first. Use &lt;code>document.execCommand('copy')&lt;/code> only as a fallback if the Clipboard API throws an error.&lt;/li>
&lt;li>&lt;strong>Full-screen Check&lt;/strong>: Use &lt;code>document.fullscreenElement&lt;/code> to detect if the user is in full-screen mode. Since the fallback won&amp;rsquo;t work in this state, you should handle this explicitly (e.g., by notifying the user to exit full-screen if the modern API fails).&lt;/li>
&lt;/ol></description></item><item><title>How to Use Hugo Blox (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/09/24/use-hugoblox/</link><pubDate>Wed, 24 Sep 2025 12:15:03 +0800</pubDate><guid>https://en.1991421.cn/2025/09/24/use-hugoblox/</guid><description>&lt;blockquote>
&lt;p>After migrating my blog from Hexo to Hugo, I settled on the Hugo Blox (formerly Wowchemy) theme framework. After six months of use, I&amp;rsquo;m quite happy with it. Here are some tips and insights from my experience.&lt;/p>
&lt;/blockquote>
&lt;h2 id="upgrading-hugo-blox">
&lt;a class="heading-anchor-link" href="#upgrading-hugo-blox">Upgrading Hugo Blox&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrading-hugo-blox"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It&amp;rsquo;s a good practice to check for updates every few months. This prevents your project from falling too far behind the upstream repository, simplifying future maintenance.&lt;/p>
&lt;p>To update the module:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hugo mod get -u
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note: This command updates the blog template framework modules, not Hugo itself. To update the Hugo binary, use your system&amp;rsquo;s package manager (e.g., &lt;code>brew upgrade hugo&lt;/code> on macOS).&lt;/em>&lt;/p>
&lt;h2 id="recommended-service-integrations">
&lt;a class="heading-anchor-link" href="#recommended-service-integrations">Recommended Service Integrations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-service-integrations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Hugo Blox comes with many features, but I’ve found that adding a few external services enhances the experience. These typically require a bit of custom code:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Comments&lt;/strong>: &lt;a href="https://waline.js.org/" target="_blank" rel="noopener">Waline&lt;/a> (a great self-hosted option).&lt;/li>
&lt;li>&lt;strong>Analytics&lt;/strong>: &lt;a href="https://www.51.la/" target="_blank" rel="noopener">51.la&lt;/a> (or any other lightweight tracking script).&lt;/li>
&lt;/ol>
&lt;h2 id="customization-adding-your-own-code">
&lt;a class="heading-anchor-link" href="#customization-adding-your-own-code">Customization: Adding Your Own Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="customization-adding-your-own-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Hooks&lt;/strong>: Hugo Blox provides hooks that allow you to inject custom HTML into specific parts of the page without modifying the core theme. Create code snippets in the appropriate directory (e.g., &lt;code>layouts/partials/hooks/&lt;/code>). Multiple files will be executed in alphabetical order. Refer to the &lt;a href="https://docs.hugoblox.com/reference/extend/#hooks" target="_blank" rel="noopener">official documentation&lt;/a> for the list of available hooks.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-09-24-160314.jpeg"
alt="Hugo Blox Hooks Directory"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>&lt;strong>New Layouts&lt;/strong>: You can add entirely new layouts under the &lt;code>layouts/&lt;/code> directory.&lt;/li>
&lt;li>&lt;strong>Overriding Defaults&lt;/strong>: To modify an existing layout, copy the file from the vendor directory (e.g., &lt;code>_vendor/github.com/HugoBlox/hugo-blox-builder/modules/blox-tailwind/layouts/_default/single.html&lt;/code>) to your project’s &lt;code>layouts/&lt;/code> directory (e.g., &lt;code>layouts/_default/single.html&lt;/code>) and edit it there. Hugo will prioritize your local version over the vendor version.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Have fun building your site!&lt;/p>
&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.hugoblox.com/" target="_blank" rel="noopener">Hugo Blox Official Documentation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use Codex (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/09/14/codex-usage-guide/</link><pubDate>Sun, 14 Sep 2025 21:28:58 +0800</pubDate><guid>https://en.1991421.cn/2025/09/14/codex-usage-guide/</guid><description>&lt;blockquote>
&lt;p>Codex is OpenAI&amp;rsquo;s coding tool and also provides a CLI. Because I subscribe to ChatGPT Plus, I can also use Codex. Here is a quick guide to using it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="prerequisites">
&lt;a class="heading-anchor-link" href="#prerequisites">Prerequisites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prerequisites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You need working network access so it functions normally. For example, the terminal must be able to reach OpenAI services.&lt;/p>
&lt;h2 id="codex-paid-usage">
&lt;a class="heading-anchor-link" href="#codex-paid-usage">Codex paid usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="codex-paid-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Claude Code is paid, with two ways to use it:&lt;/p>
&lt;ol>
&lt;li>You need an OpenAI account and a ChatGPT Plus subscription.&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>GPT costs USD $20/month. There are still some lower-price regions, but they may be canceled soon. I currently subscribe via the Turkey App Store at around RMB 100/month.&lt;/li>
&lt;/ul>
&lt;ol start="2">
&lt;li>Use an OpenAI API key and pay per usage.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-09-14-213218.jpeg"
alt="Codex Usage Guide - Fig 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="install-codex-cli">
&lt;a class="heading-anchor-link" href="#install-codex-cli">Install codex-cli&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-codex-cli"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm i -g @openai/codex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="sign-in">
&lt;a class="heading-anchor-link" href="#sign-in">Sign in&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sign-in"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">codex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Follow the prompts to choose a login type and sign in. After a successful login, you can use it normally.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-09-14-213928.jpeg"
alt="Codex Usage Guide - Fig 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="permissions">
&lt;a class="heading-anchor-link" href="#permissions">Permissions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="permissions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Codex, like cc, will ask for approval on sensitive operations such as &lt;code>scp&lt;/code> and &lt;code>rm&lt;/code>. If you want to skip these prompts, add this flag when you start the command. Then it will not ask again during the session.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">codex --dangerously-bypass-approvals-and-sandbox
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="usage-limits">
&lt;a class="heading-anchor-link" href="#usage-limits">Usage limits&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-limits"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Codex and cc both have usage limits.&lt;/p>
&lt;p>Local tasks: regular users can send 30 to 150 messages every 5 hours, with a weekly cap.
Cloud tasks: temporarily enjoy looser usage limits.
Best for: developers who need a few focused programming sessions each week.&lt;/p>
&lt;p>For details, see the &lt;a href="https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan" target="_blank" rel="noopener">official docs&lt;/a>&lt;/p>
&lt;h3 id="check-usage">
&lt;a class="heading-anchor-link" href="#check-usage">Check usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="check-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After installing the Codex extension in VS Code, you can view usage.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-11-11-155500.jpeg"
alt="Codex Usage Guide - Fig 3"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="initial-impressions">
&lt;a class="heading-anchor-link" href="#initial-impressions">Initial impressions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="initial-impressions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>It is much slower than Claude in terms of speed.&lt;/li>
&lt;li>Auto-execution feels better than Claude&amp;rsquo;s Sonnet model.&lt;/li>
&lt;li>Missing features: executing shell commands and automatic CLI upgrades. Both are must-haves for me.&lt;/li>
&lt;li>Code generation is still uncertain; I need more use before drawing conclusions.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>If you already subscribe to GPT Plus, give it a try so you do not waste the benefit.&lt;/li>
&lt;li>For general AI CLI users, cc or Codex is a matter of preference. One is enough.&lt;/li>
&lt;/ol>
&lt;h2 id="related-docs">
&lt;a class="heading-anchor-link" href="#related-docs">Related Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan" target="_blank" rel="noopener">https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://openai.com/codex/" target="_blank" rel="noopener">https://openai.com/codex/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Claude Code: Install ccstatusline to Monitor Usage in Real Time</title><link>https://en.1991421.cn/2025/08/28/claude-code-install-ccstatusline-component-to-monitor-usage-in-real-time/</link><pubDate>Thu, 28 Aug 2025 09:00:17 +0800</pubDate><guid>https://en.1991421.cn/2025/08/28/claude-code-install-ccstatusline-component-to-monitor-usage-in-real-time/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-28-090204.jpeg"
alt="Claude Code ccstatusline - Fig 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Add the following configuration to &lt;code>~/.claude/settings.json&lt;/code> to view Claude Code usage, model info, and more. If the file does not exist, create it manually.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;statusLine&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx ccstatusline@latest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;padding&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: running &lt;code>npx ccstatusline@latest&lt;/code> in the terminal also lets you customize the status line. Follow the prompts after it starts.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-28-090227.jpeg"
alt="Claude Code ccstatusline - Fig 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>Claude Code Is Expensive? Try Connecting to DeepSeek</title><link>https://en.1991421.cn/2025/08/22/claudecode-deepseek/</link><pubDate>Fri, 22 Aug 2025 21:13:36 +0800</pubDate><guid>https://en.1991421.cn/2025/08/22/claudecode-deepseek/</guid><description>&lt;blockquote>
&lt;p>I subscribe to Claude Pro, but the quota is limited and often runs out. Yesterday DeepSeek released v3.1 and also provided a ClaudeCode-compatible API. Today I tried connecting Claude Code to DeepSeek. If you have network issues or insufficient quota for ClaudeCode, you can try DS.
I think it is pretty good, so I am noting the steps here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="shell-configuration">
&lt;a class="heading-anchor-link" href="#shell-configuration">Shell configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="shell-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Open &lt;code>~/.zshrc&lt;/code> or &lt;code>~/.bashrc&lt;/code> and add the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">ANTHROPIC_BASE_URL&lt;/span>&lt;span class="o">=&lt;/span>https://api.deepseek.com/anthropic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">ANTHROPIC_AUTH_TOKEN&lt;/span>&lt;span class="o">=&lt;/span>sk-
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">ANTHROPIC_MODEL&lt;/span>&lt;span class="o">=&lt;/span>deepseek-chat
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">ANTHROPIC_SMALL_FAST_MODEL&lt;/span>&lt;span class="o">=&lt;/span>deepseek-chat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For &lt;code>ANTHROPIC_AUTH_TOKEN&lt;/code>, get it at &lt;a href="https://platform.deepseek.com/api_keys" target="_blank" rel="noopener">https://platform.deepseek.com/api_keys&lt;/a>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-22_21-29-38.jpg"
alt="api key"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After configuring, restart your terminal session or run &lt;code>source ~/.zshrc&lt;/code> or &lt;code>source ~/.bashrc&lt;/code> to apply the changes.&lt;/p>
&lt;h2 id="launch-claude-code">
&lt;a class="heading-anchor-link" href="#launch-claude-code">Launch Claude Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="launch-claude-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Type &lt;code>claude&lt;/code> in the terminal. If the proxy is configured correctly, you will see the proxied service URL in the output.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">│ • API Base URL: │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ https://api.deepseek.com/anthropic
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-22_21-32-16.jpg"
alt="api key"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Now you can use the DeepSeek version of ClaudeCode. The experience is basically the same as Claude, since only the model is changed.&lt;/p>
&lt;h2 id="flexible-switching-between-deepseek-and-claude">
&lt;a class="heading-anchor-link" href="#flexible-switching-between-deepseek-and-claude">Flexible switching between DeepSeek and Claude&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="flexible-switching-between-deepseek-and-claude"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the method above, you can only use DeepSeek models. If you want to switch back to Claude, you would have to comment out the environment variables and restart the terminal. That is too annoying, so here is a more flexible approach.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">alias claudeds=&amp;#34;claude --settings &amp;#39;{\&amp;#34;env\&amp;#34;:{\&amp;#34;ANTHROPIC_BASE_URL\&amp;#34;:\&amp;#34;https://api.deepseek.com/anthropic\&amp;#34;,\&amp;#34;ANTHROPIC_AUTH_TOKEN\&amp;#34;:\&amp;#34;sk-\&amp;#34;,\&amp;#34;ANTHROPIC_MODEL\&amp;#34;:\&amp;#34;deepseek-chat\&amp;#34;,\&amp;#34;ANTHROPIC_SMALL_FAST_MODEL\&amp;#34;:\&amp;#34;deepseek-chat\&amp;#34;}}&amp;#39;&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After setting this alias, in any terminal session, use &lt;code>claudeds&lt;/code> for DeepSeek and &lt;code>claude&lt;/code> for Claude. No need to toggle environment variables.&lt;/p>
&lt;p>Note: settings passed via command line have the highest priority, but they still merge with config files in the current directory and in the user home directory.&lt;/p>
&lt;h2 id="current-ds-issues">
&lt;a class="heading-anchor-link" href="#current-ds-issues">Current DS issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-ds-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After using it, I think DS is still a bit behind.&lt;/p>
&lt;ol>
&lt;li>Request speed is slower than Claude, but acceptable.&lt;/li>
&lt;li>AI understanding is weaker. For example, if you ask it to translate a Markdown article without modifying certain meta fields, it still changes them. Code generation is also not as good as Claude. But it is cheaper, so for general scenarios it is fine.&lt;/li>
&lt;/ol>
&lt;h2 id="pricing-note">
&lt;a class="heading-anchor-link" href="#pricing-note">Pricing note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pricing-note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>DeepSeek API gives some free credit at registration. If it runs out, you need to top up. DS is cheaper than Claude, so costs are lower. Still, keep an eye on it because Claude Code consumes tokens.&lt;/p>
&lt;p>I used DS for one hour to translate an article and spent a bit over 3 RMB. Not bad.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I think it is great that DS provides a compatible API. It saves the effort of building a separate CLI and gives users a cheaper way to use ClaudeCode. Although DS is still slower and weaker in code quality than Claude, it is good enough for general usage. If you have not tried it, give it a shot.&lt;/p></description></item><item><title>How to Use Corepack (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/08/12/use-corepack/</link><pubDate>Tue, 12 Aug 2025 10:47:17 +0800</pubDate><guid>https://en.1991421.cn/2025/08/12/use-corepack/</guid><description>&lt;blockquote>
&lt;p>While browsing the metamask-extension source code, I saw a field in package.json: &amp;ldquo;packageManager&amp;rdquo;: &amp;ldquo;&lt;a href="mailto:yarn@4.9.1">yarn@4.9.1&lt;/a>&amp;rdquo;, but when checking yarn on npm, the latest version is still 1.22.22. Strange, so how does the actual project install this v4 yarn? After careful investigation, I found that it uses &lt;code>corepack&lt;/code> for package manager and version management.&lt;/p>
&lt;/blockquote>
&lt;h2 id="description">
&lt;a class="heading-anchor-link" href="#description">Description&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="description"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Corepack is an official Node.js project, not third-party. The current latest version is &lt;code>v0.34.0&lt;/code>.&lt;/p>
&lt;h2 id="how-to-use-corepack">
&lt;a class="heading-anchor-link" href="#how-to-use-corepack">How to use corepack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-use-corepack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">corepack&lt;/span> &lt;span class="nx">enable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">#&lt;/span> &lt;span class="nx">Use&lt;/span> &lt;span class="nx">yarn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">pnpm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">npm&lt;/span> &lt;span class="nx">etc&lt;/span>&lt;span class="p">.&lt;/span> &lt;span class="nx">normally&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kn">package&lt;/span> &lt;span class="nx">installation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">yarn&lt;/span> &lt;span class="nx">install&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">pnpm&lt;/span> &lt;span class="nx">install&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">npm&lt;/span> &lt;span class="nx">install&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When executing these package commands, corepack will automatically switch to the specified version. If the package manager or that version of the package manager doesn&amp;rsquo;t exist, it will automatically install and switch to use it.&lt;/p>
&lt;h2 id="is-corepack-built-in">
&lt;a class="heading-anchor-link" href="#is-corepack-built-in">Is corepack built-in?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-corepack-built-in"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Starting from Node.js v16.9.0, Corepack is a built-in feature of Node.js&lt;/p>
&lt;h2 id="is-corepack-enabled-by-default">
&lt;a class="heading-anchor-link" href="#is-corepack-enabled-by-default">Is corepack enabled by default?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-corepack-enabled-by-default"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Actual testing shows that even Node.js v20 doesn&amp;rsquo;t have it enabled by default, you still need to manually &lt;code>corepack enable&lt;/code>.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I personally think corepack does solve a pain point, because different versions of package managers have different behaviors when installing dependencies, often causing lock file changes, which is super annoying. This way everyone uses unified package managers and versions, so lock file changes become very stable. Of course, if you need to use package managers like yarn outside of projects, you still need to install them globally yourself.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/nodejs/corepack" target="_blank" rel="noopener">https://github.com/nodejs/corepack&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Claude Pro US Subscription Guide: How to Subscribe, Pay, and Use Claude Code</title><link>https://en.1991421.cn/2025/08/04/claude-pro-subscription/</link><pubDate>Mon, 04 Aug 2025 18:16:02 +0800</pubDate><guid>https://en.1991421.cn/2025/08/04/claude-pro-subscription/</guid><description>&lt;blockquote>
&lt;p>Two things pushed me to subscribe to Claude Pro: first, Claude Code had become too useful to ignore; second, GPTs kept failing on system-prompt-heavy tasks, so I wanted to try Claude seriously. This post records how I solved the US-region Claude Pro subscription flow.&lt;/p>
&lt;/blockquote>
&lt;h2 id="prerequisites">
&lt;a class="heading-anchor-link" href="#prerequisites">Prerequisites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prerequisites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Network access: Since Claude AI service is abroad, if you have difficulty accessing the web version, Claude Code will be the same. It&amp;rsquo;s recommended to solve the network issue first.&lt;/li>
&lt;li>Non-China Apple account, such as a US Apple account.&lt;/li>
&lt;li>Visa credit card: A credit card from mainland China works fine.&lt;/li>
&lt;/ul>
&lt;h2 id="purchasing-gift-cards">
&lt;a class="heading-anchor-link" href="#purchasing-gift-cards">Purchasing Gift Cards&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="purchasing-gift-cards"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It&amp;rsquo;s recommended to purchase directly from Apple&amp;rsquo;s official website, which supports Visa payment. For specific usage tutorials, you can refer to &lt;a href="https://1991421.cn/2022/01/23/9bda4576/" target="_blank" rel="noopener">US Apple ID Payment Issues&lt;/a>&lt;/p>
&lt;h2 id="redeeming-gift-cards">
&lt;a class="heading-anchor-link" href="#redeeming-gift-cards">Redeeming Gift Cards&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redeeming-gift-cards"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Log into your Apple ID account, go to account settings, click &amp;ldquo;Redeem Gift Card or Code&amp;rdquo;, and enter the purchased gift card code.&lt;/p>
&lt;h2 id="download-and-install-claude-pro">
&lt;a class="heading-anchor-link" href="#download-and-install-claude-pro">Download and Install Claude Pro&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download-and-install-claude-pro"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Works on both mobile and PC. Simply search for &amp;ldquo;Claude&amp;rdquo; in the App Store.&lt;/p>
&lt;p>&lt;a href="https://apps.apple.com/us/app/claude-by-anthropic/id6473753684" target="_blank" rel="noopener">https://apps.apple.com/us/app/claude-by-anthropic/id6473753684&lt;/a>&lt;/p>
&lt;h2 id="subscription">
&lt;a class="heading-anchor-link" href="#subscription">Subscription&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="subscription"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you do not want a long-term subscription, just subscribe for one month, complete the payment, and cancel afterward. That prevents automatic renewal when the month ends. I do not recommend paying annually here. Monthly is enough.&lt;/p>
&lt;p>Following the above steps, you can successfully subscribe to Claude Pro.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-01-181802.jpeg"
alt="Claude Pro US subscription screen"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="claude-pro-benefits">
&lt;a class="heading-anchor-link" href="#claude-pro-benefits">Claude Pro Benefits&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="claude-pro-benefits"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Use the Claude client to access Claude Chat models like Sonnet, etc.&lt;/li>
&lt;li>Use Claude Code in the terminal without additional payment. Since it&amp;rsquo;s terminal-based, it&amp;rsquo;s cross-platform - you can use Claude Code wherever there&amp;rsquo;s a terminal.&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I personally think Claude is currently the strongest coding model. For other aspects, GPT is still stronger. Everyone can use them according to their needs.&lt;/p></description></item><item><title>Mini-Program Avatar and Nickname Retrieval</title><link>https://en.1991421.cn/2025/08/04/miniprogram-avatar-nickname/</link><pubDate>Mon, 04 Aug 2025 15:30:00 +0800</pubDate><guid>https://en.1991421.cn/2025/08/04/miniprogram-avatar-nickname/</guid><description>&lt;blockquote>
&lt;p>Recently, while working on mini-programs, I discovered changes in how WeChat mini-programs retrieve user avatars and nicknames. Of course, the official documentation can easily mislead people. Here I&amp;rsquo;ll mark the current solution.&lt;/p>
&lt;/blockquote>
&lt;p>The current solution is as follows:&lt;/p>
&lt;h2 id="wxgetuserprofile---server-side-decryption-to-get-user-information">
&lt;a class="heading-anchor-link" href="#wxgetuserprofile---server-side-decryption-to-get-user-information">wx.getUserProfile - Server-side Decryption to Get User Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wxgetuserprofile---server-side-decryption-to-get-user-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Note: wx.getUserProfile no longer shows authorization popup, but will normally retrieve encrypted_data/code/iv information. The userinfo in the message body is only for compatibility with older versions, do not use it.&lt;/p>
&lt;h2 id="mini-program-side-support-for-user-modifying-avatarnickname-information">
&lt;a class="heading-anchor-link" href="#mini-program-side-support-for-user-modifying-avatarnickname-information">Mini-program Side Support for User Modifying Avatar/Nickname Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mini-program-side-support-for-user-modifying-avatarnickname-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-avatar-retrieval">
&lt;a class="heading-anchor-link" href="#1-avatar-retrieval">1. Avatar Retrieval&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-avatar-retrieval"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use &lt;code>&amp;lt;button open-type=&amp;quot;chooseAvatar&amp;quot;&amp;gt;&lt;/code> to let users actively select avatars:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- wxml --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">button&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;avatar-wrapper&amp;#34;&lt;/span> &lt;span class="na">open-type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;chooseAvatar&amp;#34;&lt;/span> &lt;span class="na">bind:chooseavatar&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;onChooseAvatar&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">image&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;avatar&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{avatarUrl || &amp;#39;/images/default-avatar.png&amp;#39;}}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">Page&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">avatarUrl&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onChooseAvatar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">avatarUrl&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">detail&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setData&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">avatarUrl&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">avatarUrl&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Optional: upload to server
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">uploadAvatar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">avatarUrl&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">uploadAvatar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">tempFilePath&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">uploadFile&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;https://your-server.com/upload&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filePath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">tempFilePath&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;avatar&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">success&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Avatar upload successful&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-nickname-retrieval">
&lt;a class="heading-anchor-link" href="#2-nickname-retrieval">2. Nickname Retrieval&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-nickname-retrieval"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use &lt;code>&amp;lt;input type=&amp;quot;nickname&amp;quot;&amp;gt;&lt;/code> to let users input nicknames:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- wxml --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">form&lt;/span> &lt;span class="na">bindsubmit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;onSubmit&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;nickname&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;weui-input&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">placeholder&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Please enter nickname&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{nickname}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">bindinput&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;onNicknameInput&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">button&lt;/span> &lt;span class="na">formType&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;submit&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Save&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">form&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">Page&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">nickname&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onNicknameInput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setData&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">nickname&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">detail&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onSubmit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">nickname&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">detail&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">nickname&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showToast&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Please enter nickname&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;none&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">saveUserInfo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nickname&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saveUserInfo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nickname&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Save to local storage or server
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">wx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setStorageSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;nickname&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">nickname&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showToast&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Save successful&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>WeChat mini-program documentation is still as terrible as ever. We can only be disgusted while using it. Take care.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developers.weixin.qq.com/community/develop/doc/00022c683e8a80b29bed2142b56c01" target="_blank" rel="noopener">https://developers.weixin.qq.com/community/develop/doc/00022c683e8a80b29bed2142b56c01&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html" target="_blank" rel="noopener">https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use Claude Code (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/08/01/claude-code-guide/</link><pubDate>Fri, 01 Aug 2025 17:27:10 +0800</pubDate><guid>https://en.1991421.cn/2025/08/01/claude-code-guide/</guid><description>&lt;blockquote>
&lt;p>After Cursor went viral, Claude Code quickly followed. I never expected terminal-based coding to be this powerful. I tried it out of curiosity, hit a snag on the very first step, but after a few rounds of tinkering I finally got it working. It is really good. Here are the basics I noted down.&lt;/p>
&lt;/blockquote>
&lt;h2 id="prerequisites">
&lt;a class="heading-anchor-link" href="#prerequisites">Prerequisites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prerequisites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Network access: Claude AI is hosted overseas. If you cannot access the web version, Claude Code will face the same issue. Solve connectivity first.&lt;/li>
&lt;li>A virtual card or a non-mainland Apple account: Either one works. The reasons are explained below.&lt;/li>
&lt;/ul>
&lt;h2 id="claude-code-paid-usage">
&lt;a class="heading-anchor-link" href="#claude-code-paid-usage">Claude Code Paid Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="claude-code-paid-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Claude Code is a paid service, with two usage options:&lt;/p>
&lt;ol>
&lt;li>Claude subscription users, such as &lt;strong>Claude Pro&lt;/strong> or higher Max plans:
&lt;ul>
&lt;li>I find Apple App Store subscription the most convenient. For example, I buy gift cards from Apple&amp;rsquo;s official website, log into a US Apple account on my iPhone, and subscribe directly. For how to purchase apps in the US region, see &lt;a href="https://1991421.cn/2022/01/23/9bda4576/" target="_blank" rel="noopener">US Apple ID Payment Issues&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://console.anthropic.com/" target="_blank" rel="noopener">Anthropic Console&lt;/a> account: bind a credit card for pay-as-you-go billing. If you do not have a US credit card, you will need a virtual card. I used nobepay before, but they stopped accepting new card applications, so I do not recommend it now.&lt;/li>
&lt;/ol>
&lt;p>I personally chose the &lt;strong>Claude Pro&lt;/strong> subscription since it includes other features. My GPT subscription happened to expire, so I decided not to renew GPT for now.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-01-181802.jpeg"
alt="Claude Code Guide - Fig 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="claude-installation">
&lt;a class="heading-anchor-link" href="#claude-installation">Claude Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="claude-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For installation, just run the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">curl&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="nx">fsSL&lt;/span> &lt;span class="nx">https&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="c1">//claude.ai/install.sh | bash
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The npm installation method has been deprecated by the official team and is not recommended.&lt;/p>
&lt;h2 id="terminal-proxy">
&lt;a class="heading-anchor-link" href="#terminal-proxy">Terminal Proxy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="terminal-proxy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>PC global proxy&lt;/li>
&lt;li>Shell proxy configuration
&lt;ul>
&lt;li>
&lt;p>Set environment variables in your terminal profile (&lt;code>~/.bashrc&lt;/code> or &lt;code>~/.zshrc&lt;/code>) using the following commands:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">http_proxy&lt;/span>&lt;span class="o">=&lt;/span>http://127.0.0.1:6152
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$http_proxy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">all_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$http_proxy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">HTTPS_PROXY&lt;/span>&lt;span class="o">=&lt;/span>http://127.0.0.1:6152
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">disproxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;unset http_proxy https_proxy all_proxy&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Claude usage is fairly simple at the basic level.&lt;/p>
&lt;ol>
&lt;li>Type &lt;code>claude&lt;/code>. On first use you will go through the login flow. Choose the appropriate method to sign in.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-08-02-230811.jpeg"
alt="Claude Code Guide - Fig 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>Enter interactive mode, describe your task, and Claude will identify and apply changes. Repeat as needed.&lt;/li>
&lt;/ol>
&lt;p>For advanced usage, refer to the official documentation.&lt;/p>
&lt;h3 id="usage-monitoring">
&lt;a class="heading-anchor-link" href="#usage-monitoring">Usage Monitoring&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-monitoring"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use &lt;code>/usage&lt;/code>, or check it inside the Claude app.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/2026-01-05-183756.jpeg"
alt="https://static.1991421.cn/2026/2026-01-05-183756.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="auto-accept-edits">
&lt;a class="heading-anchor-link" href="#auto-accept-edits">Auto-Accept Edits&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="auto-accept-edits"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Either enable it globally in settings, or pass the parameter when launching from the command line.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">claude --permission-mode acceptEdits
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="using-agentsskills">
&lt;a class="heading-anchor-link" href="#using-agentsskills">Using Agents/Skills&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-agentsskills"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Personally, to save tokens, I do not want to use Claude to translate my blog. I can configure a DeepSeek translation agent so Claude Code will automatically call DeepSeek for translation tasks, which saves tokens. There are a few ways to trigger this.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>Manually: in interactive mode, use &lt;code>@agent&lt;/code> or &lt;code>skills&lt;/code>, or explicitly mention the tool in your prompt.&lt;/li>
&lt;li>System prompt convention: define that a certain scenario should use the target agent/skill. After configuration, the AI will automatically call the agent/skill during real interactions.&lt;/li>
&lt;/ol>
&lt;h3 id="mcp">
&lt;a class="heading-anchor-link" href="#mcp">MCP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mcp"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>MCP is for extending CC capabilities.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List installed MCPs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">claude mcp list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note:&lt;/p>
&lt;ol>
&lt;li>If an MCP does not work, it is likely not connected. The command above also performs a health check. If it cannot connect, copy the MCP command and run it directly to verify.&lt;/li>
&lt;li>Some MCPs require higher Node versions; consider upgrading Node to the latest.&lt;/li>
&lt;/ol>
&lt;h2 id="permissions">
&lt;a class="heading-anchor-link" href="#permissions">Permissions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="permissions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Whether it is MCP or CC itself, permissions are always an issue. Manual approval is safer but tedious. For some operations, you can add a direct allowlist.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Grant all tool permissions under a specific MCP&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;mcp__chrome-devtools&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="bash-command-suggestions">
&lt;a class="heading-anchor-link" href="#bash-command-suggestions">Bash Command Suggestions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bash-command-suggestions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In earlier versions, CC said it removed command completion. I could not get it to work then. In recent versions, history-based command suggestions are supported, and it finally worked for me. Maybe the earlier release only had underlying support, while this time it is truly usable. Note this is history-only. Compared to traditional shells, this is autosuggestion, not autocomplete. True autocomplete should offer multiple options you choose from.&lt;/p>
&lt;p>The effect looks like this.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2026/2026-01-25-115935.png"
alt="https://static.1991421.cn/2026/2026-01-25-115935.png"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note&lt;/p>
&lt;ol>
&lt;li>There will be no suggestions at first. You must type and successfully run a command once for CC to record it, then it can suggest next time.&lt;/li>
&lt;li>CC&amp;rsquo;s bash history is stored separately and does not use your zsh/bash history file. You can clear the global history and still see suggestions here.&lt;/li>
&lt;li>Because this is autosuggestion, it only suggests one item at a time. As you adjust input, the suggestion changes.&lt;/li>
&lt;/ol>
&lt;p>Personally, it would be even better if it could show full command completion suggestions. I hope CC keeps improving.&lt;/p>
&lt;h3 id="solo-mode">
&lt;a class="heading-anchor-link" href="#solo-mode">Solo Mode&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solo-mode"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For sensitive operations like &lt;code>scp&lt;/code>, &lt;code>rm&lt;/code>, etc., there will be authorization prompts. You can choose not to be reminded again, which essentially adds that operation to the project allowlist. Other operations will still prompt. If you want a one-time solution, you can grant full permissions to the AI, which is equivalent to Gemini&amp;rsquo;s Solo mode.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">claude --dangerously-skip-permissions
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The Claude Code experience is quite good right now. While you may run into network issues in China, overall it is very convenient. I hope this guide helps those who need it.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.anthropic.com/en/docs/claude-code/overview" target="_blank" rel="noopener">https://docs.anthropic.com/en/docs/claude-code/overview&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.anthropic.com/engineering/claude-code-best-practices" target="_blank" rel="noopener">https://www.anthropic.com/engineering/claude-code-best-practices&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Tailwind Explained (Simple Guide)</title><link>https://en.1991421.cn/2025/07/24/learn-tailwind/</link><pubDate>Thu, 24 Jul 2025 18:10:37 +0800</pubDate><guid>https://en.1991421.cn/2025/07/24/learn-tailwind/</guid><description>&lt;blockquote>
&lt;p>Recently, an open source project needed an H5 version, so I decided to try Tailwind. Here I&amp;rsquo;ll mark down my understanding and feelings after using it.&lt;/p>
&lt;/blockquote>
&lt;p>For learning materials, the official website is of course the top recommendation. Here I&amp;rsquo;ll mainly share my rough understanding.&lt;/p>
&lt;h2 id="positioning">
&lt;a class="heading-anchor-link" href="#positioning">Positioning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="positioning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>CSS framework, not a UI component library, and not UI component CSS. For example, it&amp;rsquo;s not equivalent to Bootstrap.&lt;/li>
&lt;/ul>
&lt;h2 id="usage-methods">
&lt;a class="heading-anchor-link" href="#usage-methods">Usage Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Direct CDN loading of JS is possible, but the downside is full loading, so file size needs to be considered.&lt;/li>
&lt;li>PostCSS plugin form, very convenient to integrate with build tools like Webpack, Vite. This method generates final CSS during the build process, on-demand loading, smaller size.&lt;/li>
&lt;/ul>
&lt;h2 id="characteristics">
&lt;a class="heading-anchor-link" href="#characteristics">Characteristics&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="characteristics"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Atomic CSS, the advantage is that on-demand use is very comfortable, can combine many styles, the downside is that there will be many class names&lt;/li>
&lt;li>Style names are very semantic, after playing with it more you&amp;rsquo;ll become familiar and can get started quickly&lt;/li>
&lt;/ul>
&lt;h2 id="actual-projects-used">
&lt;a class="heading-anchor-link" href="#actual-projects-used">Actual Projects Used&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="actual-projects-used"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Recent mini-program project using Taro+Webpack, Taro supports Tailwind, so it was very convenient to integrate and use.&lt;/li>
&lt;li>For mobile web H5 projects, because Webpack is used, Tailwind can also be integrated.&lt;/li>
&lt;/ol>
&lt;h2 id="tailwind-vs-bootstrap">
&lt;a class="heading-anchor-link" href="#tailwind-vs-bootstrap">Tailwind vs Bootstrap&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tailwind-vs-bootstrap"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The comparison isn&amp;rsquo;t quite appropriate, but for beginner users, it&amp;rsquo;s helpful to understand the differences.&lt;/p>
&lt;ul>
&lt;li>Bootstrap is a UI component library, meaning the final product is a pre-designed button that follows the Bootstrap design system.&lt;/li>
&lt;li>Tailwind is a CSS utility library, meaning the final product is a set of CSS classes defined semantically, allowing you to build your own buttons in a semantic way.&lt;/li>
&lt;/ul>
&lt;p>These two are different tools for different use cases.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I personally think Tailwind is simple and pure enough to make it a great CSS framework. In actual projects, it doesn&amp;rsquo;t interfere with using other UI component library styles, so it&amp;rsquo;s a great tool that&amp;rsquo;s indeed suitable for rapid development.&lt;/p></description></item><item><title>Keyof for Nested Objects</title><link>https://en.1991421.cn/2025/07/22/nested-keyof/</link><pubDate>Tue, 22 Jul 2025 18:27:32 +0800</pubDate><guid>https://en.1991421.cn/2025/07/22/nested-keyof/</guid><description>&lt;h2 id="problem">
&lt;a class="heading-anchor-link" href="#problem">Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">interface&lt;/span> &lt;span class="n">User&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">age&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">type&lt;/span> &lt;span class="n">Column&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">keyof&lt;/span> &lt;span class="n">User&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="n">columns&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Column&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;name1&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s1">&amp;#39;parent.name&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">type&lt;/span> &lt;span class="nx">Paths&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">T&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="kt">object&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">K&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="k">keyof&lt;/span> &lt;span class="nx">T&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">Exclude&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">K&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">symbol&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="si">}${&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="sb">`.&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">Paths&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="err">[&lt;/span>&lt;span class="na">K&lt;/span>&lt;span class="err">]&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}[&lt;/span>&lt;span class="k">keyof&lt;/span> &lt;span class="nx">T&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="kt">never&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">type&lt;/span> &lt;span class="nx">Column&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>: &lt;span class="kt">Paths&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In addition to writing your own &lt;code>Paths&lt;/code> type, you can also use existing libraries, such as the &lt;code>Path&lt;/code> type from &lt;code>type-fest&lt;/code>.&lt;/p>
&lt;h2 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">columns&lt;/span>: &lt;span class="kt">Column&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;parent.name&amp;#39;&lt;/span> &lt;span class="c1">// ok
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;parent.name1&amp;#39;&lt;/span> &lt;span class="c1">// error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object" target="_blank" rel="noopener">https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>NotebookLM Audio Podcast Experience Review: Current Limitations</title><link>https://en.1991421.cn/2025/06/27/podcastlm-podcast-experience-review/</link><pubDate>Fri, 27 Jun 2025 23:09:16 +0800</pubDate><guid>https://en.1991421.cn/2025/06/27/podcastlm-podcast-experience-review/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;ve been using NotebookLM for several months, and the audio overview/voice podcast feature is one I use frequently. Here&amp;rsquo;s a summary of some current limitations I&amp;rsquo;ve discovered.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-06-10-225740.jpeg"
alt="https://static.1991421.cn/2025/2025-06-10-225740.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="voice-tonespeed">
&lt;a class="heading-anchor-link" href="#voice-tonespeed">Voice Tone/Speed&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="voice-tonespeed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Currently doesn&amp;rsquo;t support prompt-based control of speech narration speed, and you can&amp;rsquo;t choose between male/female voice tones. For example, if you set the output language to Chinese, both male and female voices are fixed.&lt;/p>
&lt;h2 id="interaction-mode">
&lt;a class="heading-anchor-link" href="#interaction-mode">Interaction Mode&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="interaction-mode"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>PodcastLM&amp;rsquo;s interaction mode allows you to participate in conversations between two hosts, but it currently doesn&amp;rsquo;t support Chinese. If the output podcast is in Chinese, the interaction mode button won&amp;rsquo;t be visible.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-06-27-230933.jpeg"
alt="https://static.1991421.cn/2025/2025-06-27-230933.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="podcast-output-language-is-global-setting">
&lt;a class="heading-anchor-link" href="#podcast-output-language-is-global-setting">Podcast Output Language is Global Setting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="podcast-output-language-is-global-setting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This means if I change the output language, any new podcast output from any notebook will use that language.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Checking the official documentation, I see NotebookLM has some features coming soon. I can only hope they&amp;rsquo;re released early. If purchasing becomes necessary, as long as it&amp;rsquo;s good enough, it might be worth considering.&lt;/p></description></item><item><title>Submit URLs to Baidu via API</title><link>https://en.1991421.cn/2025/05/02/baidu-url-submission/</link><pubDate>Fri, 02 May 2025 22:33:53 +0800</pubDate><guid>https://en.1991421.cn/2025/05/02/baidu-url-submission/</guid><description>&lt;blockquote>
&lt;p>I wanted Baidu to index more of my blog posts, so I set up API submissions. Here’s the process.&lt;/p>
&lt;/blockquote>
&lt;h2 id="get-the-api-token">
&lt;a class="heading-anchor-link" href="#get-the-api-token">Get the API Token&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="get-the-api-token"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Open Baidu Search Console, select your site, then &lt;strong>Regular Inclusion → Resource Submission → API Submission&lt;/strong>. The token displayed there authorizes submissions—keep it secret.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-05-02-223610.jpeg"
alt="https://static.1991421.cn/2025/2025-05-02-223610.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="submit-urls">
&lt;a class="heading-anchor-link" href="#submit-urls">Submit URLs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="submit-urls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Example Node.js script for my Hugo blog; any language works.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">SITEMAP_FILE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;../public/sitemap.xml&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// sitemap path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">BAIDU_API_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`http://data.zz.baidu.com/urls?site=1991421.cn&amp;amp;token=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">BAIDU_ZZ_TOKEN&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Baidu submission endpoint
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">MAX_URLS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// max URLs per request
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">submitToBaidu&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">urls&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Submitting URLs&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">urls&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">BAIDU_API_URL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;POST&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">headers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;text/plain&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">body&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">urls&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// newline-delimited list
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ok&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`HTTP error: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Submit success:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Submit failed:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="my-workflow">
&lt;a class="heading-anchor-link" href="#my-workflow">My Workflow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="my-workflow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I run this script in GitHub Actions. Each build generates &lt;code>sitemap.xml&lt;/code>; the action grabs the newest N URLs and posts them to Baidu.&lt;/p>
&lt;h2 id="common-questions">
&lt;a class="heading-anchor-link" href="#common-questions">Common Questions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-questions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="daily-quota">
&lt;a class="heading-anchor-link" href="#daily-quota">Daily Quota&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="daily-quota"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>10 URLs per day.&lt;/li>
&lt;li>Exceeding the quota returns HTTP 400.&lt;/li>
&lt;/ul>
&lt;h3 id="common-400-errors">
&lt;a class="heading-anchor-link" href="#common-400-errors">Common 400 Errors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-400-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Quota exceeded&lt;/li>
&lt;li>Invalid token&lt;/li>
&lt;li>Wrong &lt;code>site&lt;/code> parameter (omit the protocol, e.g. &lt;code>site=1991421.cn&lt;/code>)&lt;/li>
&lt;/ol>
&lt;h2 id="closing-thoughts">
&lt;a class="heading-anchor-link" href="#closing-thoughts">Closing Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closing-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Baidu isn’t my favorite, but it’s still worth submitting URLs for mainland readers. If you know a better method, leave a comment.&lt;/p>
&lt;h2 id="further-reading">
&lt;a class="heading-anchor-link" href="#further-reading">Further Reading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="further-reading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://ziyuan.baidu.com/dashboard/index" target="_blank" rel="noopener">https://ziyuan.baidu.com/dashboard/index&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ziyuan.baidu.com/site/index#/" target="_blank" rel="noopener">https://ziyuan.baidu.com/site/index#/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Migrate Blog from Hexo to Hugo</title><link>https://en.1991421.cn/2025/04/30/migrate-blog-from-hexo-to-hugo/</link><pubDate>Wed, 30 Apr 2025 18:26:20 +0800</pubDate><guid>https://en.1991421.cn/2025/04/30/migrate-blog-from-hexo-to-hugo/</guid><description>&lt;blockquote>
&lt;p>I’ve used &lt;a href="https://hexo.io/zh-cn/" target="_blank" rel="noopener">Hexo&lt;/a> for 9 years. While Hexo is still alive, themes are scarce, the visuals feel tired, and customization costs are high. Over the holiday, I decided to try migrating to Hugo.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-04-30-202206.jpeg"
alt="https://static.1991421.cn/2025/2025-04-30-202206.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="steps">
&lt;a class="heading-anchor-link" href="#steps">Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="install-hugo">
&lt;a class="heading-anchor-link" href="#install-hugo">Install Hugo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-hugo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install go
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install hugo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: newer Hugo versions can sometimes cause errors. When using HugoBlox, I hit startup errors. The fix was installing &lt;code>hugo@0.134.2&lt;/code>. If you see odd issues, try downgrading.&lt;/p>
&lt;h3 id="use-hugoblox">
&lt;a class="heading-anchor-link" href="#use-hugoblox">Use HugoBlox&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-hugoblox"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>HugoBlox is a higher‑level framework that simplifies theme customization and feature development. It’s flexible and feature‑rich.&lt;/p>
&lt;p>Visit the &lt;a href="https://hugoblox.com/templates/" target="_blank" rel="noopener">HugoBlox template gallery&lt;/a> and pick a theme. Paid themes don’t have an Edit button and must be purchased. I chose a free theme and clicked Edit, which jumps to GitHub. There, create a repo from the template. Once created, proceed with theme configuration.&lt;/p>
&lt;h2 id="theme-customization">
&lt;a class="heading-anchor-link" href="#theme-customization">Theme customization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="theme-customization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Clone the repo locally. Refer to the &lt;a href="https://docs.hugoblox.com/" target="_blank" rel="noopener">official docs&lt;/a> for simple customizations.&lt;/p>
&lt;p>Examples:&lt;/p>
&lt;ol>
&lt;li>Enable site search&lt;/li>
&lt;li>Set favicon&lt;/li>
&lt;li>Configure share buttons
…there are many options.&lt;/li>
&lt;/ol>
&lt;h2 id="migrate-hexo-posts-to-hugo">
&lt;a class="heading-anchor-link" href="#migrate-hexo-posts-to-hugo">Migrate Hexo posts to Hugo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="migrate-hexo-posts-to-hugo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After theme setup, migrate posts. Since my site has run for years and URLs are indexed by search engines and other platforms, I need to keep URLs stable. That requires some work:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Change Hexo frontmatter &lt;code>description&lt;/code> → &lt;code>summary&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Change Hexo frontmatter &lt;code>addrlink&lt;/code> → &lt;code>slug&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Adjust Hugo permalink rules:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">permalinks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">blog: &amp;#39;/:year/:month/:day/:slug/&amp;#39; # Output&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/2025/04/30/my-post/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="additional-notes">
&lt;a class="heading-anchor-link" href="#additional-notes">Additional notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="additional-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="installing-an-older-hugo">
&lt;a class="heading-anchor-link" href="#installing-an-older-hugo">Installing an older Hugo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-an-older-hugo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Package managers like Homebrew may not provide older versions. If you hit the version issue above, install manually. Download a specific release from &lt;a href="https://github.com/gohugoio/hugo/releases" target="_blank" rel="noopener">GitHub&lt;/a>. For macOS, grab the &lt;code>darwin-universal.tar.gz&lt;/code> archive.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-05-01-165752.jpeg"
alt="https://static.1991421.cn/2025/2025-05-01-165752.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Copy the binary to your PATH:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> mv &lt;span class="nv">$HOME&lt;/span>/Desktop/hugo_0.134.0_darwin-universal/hugo /opt/homebrew/bin/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You may see a security warning; allow it under Privacy &amp;amp; Security. Then &lt;code>hugo version&lt;/code> should work.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-05-01-170705.jpeg"
alt="https://static.1991421.cn/2025/2025-05-01-170705.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That completes the migration — total time: one day.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://gohugo.io/" target="_blank" rel="noopener">Hugo&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hugoblox.com/" target="_blank" rel="noopener">HugoBlox&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hugoblox.com/templates/" target="_blank" rel="noopener">HugoBlox Template Gallery&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.hugoblox.com/" target="_blank" rel="noopener">HugoBlox Documentation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Dart Development Guide in VS Code</title><link>https://en.1991421.cn/2025/04/28/6b3d62f/</link><pubDate>Mon, 28 Apr 2025 11:19:22 +0800</pubDate><guid>https://en.1991421.cn/2025/04/28/6b3d62f/</guid><description>&lt;blockquote>
&lt;p>I recently started working with Flutter and chose VS Code as my primary editor. Here’s the tooling stack and setup notes.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tools">
&lt;a class="heading-anchor-link" href="#tools">Tools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>VS Code&lt;/strong> — editor
&lt;ul>
&lt;li>Install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code" target="_blank" rel="noopener">Dart extension&lt;/a> and the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter" target="_blank" rel="noopener">Flutter extension&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>FVM&lt;/strong> — Dart/Flutter version manager.&lt;/li>
&lt;li>&lt;strong>Android Studio&lt;/strong> — emulator + SDK manager.&lt;/li>
&lt;/ul>
&lt;h2 id="tips-for-the-dart-extension">
&lt;a class="heading-anchor-link" href="#tips-for-the-dart-extension">Tips for the Dart Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tips-for-the-dart-extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>After editing &lt;code>pubspec.yaml&lt;/code>, VS Code generally runs &lt;code>pub get&lt;/code> automatically. For Git dependencies, trigger &lt;strong>Pub: Upgrade Packages&lt;/strong> manually instead of deleting the lock file.&lt;/li>
&lt;li>The extension resolves the Dart SDK in this order: explicit SDK path in settings, then the global PATH (e.g., &lt;code>fvm global&lt;/code>).&lt;/li>
&lt;/ul></description></item><item><title>How to Use FVM (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/04/15/46271986/</link><pubDate>Tue, 15 Apr 2025 17:44:50 +0800</pubDate><guid>https://en.1991421.cn/2025/04/15/46271986/</guid><description>&lt;blockquote>
&lt;p>When there are many Flutter projects, you also face the same multi-version environment maintenance issues as Node.js projects. Fortunately, there&amp;rsquo;s a similar product called FVM. Here&amp;rsquo;s a simple introduction.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">brew install fvm
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="install-a-specific-version">
&lt;a class="heading-anchor-link" href="#install-a-specific-version">Install a Specific Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-a-specific-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">fvm install 3.19.2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that after installation, fvm list will show that dart and other modules are not installed, but prompt that configuration is needed. This will only install when actually used, i.e., &lt;code>fvm use&lt;/code>.&lt;/p>
&lt;h2 id="use-a-specific-version">
&lt;a class="heading-anchor-link" href="#use-a-specific-version">Use a Specific Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-a-specific-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">fvm use
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After using, you&amp;rsquo;ll find that the actual installation begins. After installation is complete, re-executing list will show the dart version information.&lt;/p>
&lt;h2 id="generate-fvm-configuration-for-project">
&lt;a class="heading-anchor-link" href="#generate-fvm-configuration-for-project">Generate FVM Configuration for Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="generate-fvm-configuration-for-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">fvm use *** --save
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that after save, the project configuration will be generated, and you can use fvm use directly afterwards.&lt;/p>
&lt;h2 id="global-package-version-setting">
&lt;a class="heading-anchor-link" href="#global-package-version-setting">Global Package Version Setting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="global-package-version-setting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes for convenience, you need to set a global version so you don&amp;rsquo;t have to use fvm commands every time. The specific setting method is as follows.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Configure PATH in zshrc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/fvm/default/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Set global version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">fvm global
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cancel global version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">fvm global -u
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://fvm.app/documentation/getting-started" target="_blank" rel="noopener">https://fvm.app/documentation/getting-started&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Options for Compiling TypeScript</title><link>https://en.1991421.cn/2025/04/02/ts-compile-build/</link><pubDate>Wed, 02 Apr 2025 18:01:02 +0800</pubDate><guid>https://en.1991421.cn/2025/04/02/ts-compile-build/</guid><description>&lt;blockquote>
&lt;p>I’ve seen projects using &lt;code>tsc&lt;/code>, &lt;code>ts-loader&lt;/code>, or &lt;code>babel-loader&lt;/code> for TypeScript builds. Here’s a short comparison so it’s clear what each does.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tsc">
&lt;a class="heading-anchor-link" href="#tsc">tsc&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tsc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official compiler. It performs type checking (unless you disable it—which defeats much of TypeScript’s value).&lt;/p>
&lt;h2 id="ts-loader">
&lt;a class="heading-anchor-link" href="#ts-loader">ts-loader&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ts-loader"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Webpack loader that delegates to &lt;code>tsc&lt;/code> under the hood. You can configure it to skip type checking for faster builds.&lt;/p>
&lt;h2 id="babel-loader">
&lt;a class="heading-anchor-link" href="#babel-loader">babel-loader&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="babel-loader"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Babel is a general-purpose compiler; TypeScript support is just one plugin. It’s fast but doesn’t type-check, so pair it with tools like ForkTsCheckerWebpackPlugin if you need type safety.&lt;/p>
&lt;h2 id="which-should-you-use">
&lt;a class="heading-anchor-link" href="#which-should-you-use">Which Should You Use?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="which-should-you-use"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Within our team we use both &lt;code>ts-loader&lt;/code> and &lt;code>babel-loader&lt;/code> depending on the project. It largely comes down to preference and build requirements.&lt;/p></description></item><item><title>Frontend Basics — P3</title><link>https://en.1991421.cn/2025/04/02/frontend-basics-p3/</link><pubDate>Wed, 02 Apr 2025 14:56:19 +0800</pubDate><guid>https://en.1991421.cn/2025/04/02/frontend-basics-p3/</guid><description>&lt;blockquote>
&lt;p>Will AI replace IT? Not anytime soon. Keep an eye on AI, but don’t forget the fundamentals — they’re the foundation, and AI is just a tool. Here’s a JS array question to illustrate some basics.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">list&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">list2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[,&lt;/span>&lt;span class="kc">undefined&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">NaN&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">item&lt;/span> &lt;span class="k">of&lt;/span> &lt;span class="nx">list&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">list2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;list2.length&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">list2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1、输出啥
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 2、list2长度是多少
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="output">
&lt;a class="heading-anchor-link" href="#output">Output&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="output"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">10
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">undefined
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">null
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">false
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NaN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="key-points">
&lt;a class="heading-anchor-link" href="#key-points">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>A leading comma creates a “hole” at the head; trailing commas don’t add an extra hole.&lt;/li>
&lt;li>When iterating, holes don’t trigger the callback, but iteration still advances the index — the first printed index is 1, not 0.&lt;/li>
&lt;/ol></description></item><item><title>Will There Be Webpack 6?</title><link>https://en.1991421.cn/2025/04/02/webpack6/</link><pubDate>Wed, 02 Apr 2025 11:35:50 +0800</pubDate><guid>https://en.1991421.cn/2025/04/02/webpack6/</guid><description>&lt;blockquote>
&lt;p>Webpack 5 has been out for a long time. Will there be a Webpack 6, or is Webpack exiting the stage? I checked the repo and found the answer.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-04-02-140251.jpeg"
alt="https://static.1991421.cn/2025/2025-04-02-140251.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Research shows v5 shipped in 2020.&lt;/em>&lt;/p>
&lt;h2 id="is-there-a-webpack-6">
&lt;a class="heading-anchor-link" href="#is-there-a-webpack-6">Is There a Webpack 6?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-there-a-webpack-6"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>YES—Webpack is far from retiring. It remains one of the most widely used build tools and is still actively developed.&lt;/p>
&lt;h2 id="roadmap-for-webpack-6">
&lt;a class="heading-anchor-link" href="#roadmap-for-webpack-6">Roadmap for Webpack 6?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="roadmap-for-webpack-6"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There’s no public v6 plan at the moment. The team is focused on stability and performance improvements for Webpack 5. Not every good feature needs a major version bump.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-04-02-135704.jpeg"
alt="https://static.1991421.cn/2025/2025-04-02-135704.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="other-bundlers-tout-performancehow-does-webpack-respond">
&lt;a class="heading-anchor-link" href="#other-bundlers-tout-performancehow-does-webpack-respond">Other bundlers tout performance—how does Webpack respond?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-bundlers-tout-performancehow-does-webpack-respond"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The team is aware and has introduced performance optimizations in Webpack 5, with ongoing improvements.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In my team, Webpack is still the primary build tool; some projects use Vite.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/webpack/webpack/issues/16377" target="_blank" rel="noopener">https://github.com/webpack/webpack/issues/16377&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use React Hook Form (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/03/31/d09754/</link><pubDate>Mon, 31 Mar 2025 14:18:19 +0800</pubDate><guid>https://en.1991421.cn/2025/03/31/d09754/</guid><description>&lt;blockquote>
&lt;p>Forms are a common interaction method in user interfaces, and validating/managing their state can be challenging.&lt;/p>
&lt;p>In the React ecosystem, &lt;a href="https://react-hook-form.com/" target="_blank" rel="noopener">React Hook Form &lt;code>package name: react-hook-form&lt;/code>&lt;/a> is a powerful solution for complex form management.
To use it flexibly and appropriately, this guide summarizes common scenarios, usage patterns, easily overlooked points, and core principles. Please provide feedback if there are any omissions.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-31-142653.jpeg"
alt="https://static.1991421.cn/2025/2025-03-31-142653.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage🔨&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In practical use, we often only use &lt;code>useForm/Controller/getValues&lt;/code>, but hook-form has other methods/configurations that are worth exploring.&lt;/p>
&lt;h3 id="mode-in-useform">
&lt;a class="heading-anchor-link" href="#mode-in-useform">mode in useForm&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mode-in-useform"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Different modes affect when form validation occurs, such as when errors are obtained after validation.&lt;/p>
&lt;p>&lt;strong>The default mode is onSubmit&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">VALIDATION_MODE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onBlur&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;onBlur&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onChange&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;onChange&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onSubmit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;onSubmit&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onTouched&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;onTouched&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">all&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;all&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="kr">const&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note: mode affects validation strategy, but the form always detects value changes. Validation strategy before submitting behaviour.&lt;/strong>&lt;/p>
&lt;h4 id="revalidatemode">reValidateMode&lt;/h4>&lt;p>Note: If mode itself is configured as onChange, then reValidateMode becomes meaningless.&lt;/p>
&lt;h3 id="useformcontext">
&lt;a class="heading-anchor-link" href="#useformcontext">useFormContext&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="useformcontext"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>hook&lt;/code> - If a form is an N-level component tree, managing complex nested form items and passing form control parameters becomes problematic. We need to pass control/setValue etc. created by create form to child components, which then pass them down to lower-level components, creating a cumbersome chain.
If using useFormContext, you can directly manage form values within components without needing to pass them down layer by layer.&lt;/p>
&lt;h4 id="layer-by-layer-passing">Layer-by-layer passing&lt;/h4>&lt;img src="https://static.1991421.cn/2025/2025-05-13-165717.jpeg" alt="https://static.1991421.cn/2025/2025-05-13-165717.jpeg" style="zoom:50%;" />
&lt;h4 id="context">Context&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">FormProvider&lt;/span> &lt;span class="p">{...&lt;/span>&lt;span class="nx">formProps&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/FormProvider&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">control&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useFormContext&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="usefieldarray">
&lt;a class="heading-anchor-link" href="#usefieldarray">useFieldArray&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usefieldarray"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>hook&lt;/code> - Sometimes data exists in array/List format. For dynamic item addition, useFieldArray is more convenient.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">fields&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">append&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">prepend&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">remove&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">swap&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">move&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">insert&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useFieldArray&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// control props comes from useForm (optional: if you are using FormProvider)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test&amp;#39;&lt;/span> &lt;span class="c1">// unique name for your Field Array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note: You can still use arrays without useFieldArray, but it&amp;rsquo;s less convenient&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Form&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Item&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;Person 0&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{...&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;persons.0&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Form.Item&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="watch">
&lt;a class="heading-anchor-link" href="#watch">watch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="watch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>watch&lt;code>func&lt;/code> - useWatch is used to monitor specific field changes or all current field values, but if you want to know which field changed each time or perform batch watching for logic processing, you can use the watch method.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">useEffect&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">wFn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">watch&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;column changed&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// data contains the latest values after modification
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">wFn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unsubscribe&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: Remember to unsubscribe when subscribing.&lt;/p>
&lt;h3 id="watch-vs-usewatch">
&lt;a class="heading-anchor-link" href="#watch-vs-usewatch">watch vs useWatch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="watch-vs-usewatch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>React-hook-form has two watch methods: useWatch and watch. useWatch is a hook method, watch is a function method. For single field subscriptions, you can use either watch or useWatch, but useWatch is recommended. WHY?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">allFieldWatch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useWatch&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;price&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="c1">// If name is not provided, it watches all fields
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">priceWatch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">watch&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;price&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="register">
&lt;a class="heading-anchor-link" href="#register">register&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="register"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>register&lt;code>func&lt;/code> - Registers a form field and returns an object. This method is primarily for native form elements. For non-native form elements like Input components in Tea component libraries, be cautious about potential issues with onChange methods.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{...&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;address&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">validate&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;address validate&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">s&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note:&lt;/p>
&lt;ol>
&lt;li>name supports nested notation, such as &lt;code>persons.0&lt;/code>, &lt;code>address.city&lt;/code>.&lt;/li>
&lt;li>For arrays, you can directly register with non-zero indices.&lt;/li>
&lt;/ol>
&lt;h3 id="register-vs-controller">
&lt;a class="heading-anchor-link" href="#register-vs-controller">register vs Controller&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="register-vs-controller"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>As mentioned above, register is suitable for native elements, while Controller is suitable for higher-order form components, such as third-party components.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Form&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Item&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;Person 0&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{...&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;persons.0&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// valueAsNumber: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">})}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Form.Item&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Form&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Item&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;Person 1&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{...&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;persons.1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// valueAsNumber: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">})}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Form.Item&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>PS: The official documentation prefers using &lt;code>register&lt;/code>.&lt;/p>
&lt;h3 id="controller-vs-usecontroller">
&lt;a class="heading-anchor-link" href="#controller-vs-usecontroller">Controller vs useController&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="controller-vs-usecontroller"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Controller essentially calls useController, so they are the same - just different usage patterns: one is a component tag, the other is a hook function.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Controller&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">TFieldValues&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">FieldValues&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">FieldValues&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">TName&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">FieldPath&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">TFieldValues&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">FieldPath&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">TFieldValues&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">TTransformedValues&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">TFieldValues&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">props&lt;/span>: &lt;span class="kt">ControllerProps&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">TFieldValues&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">TName&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">TTransformedValues&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">useController&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">TFieldValues&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">TName&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">TTransformedValues&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Controller&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="resetfunc-setvalue">
&lt;a class="heading-anchor-link" href="#resetfunc-setvalue">reset&lt;code>func&lt;/code>/ setValue&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resetfunc-setvalue"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>If reset method doesn&amp;rsquo;t specify field values, it resets to the values set in defaultValues.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">reset&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>reset operation resets all fields, not just specific ones. For example, if reset only specifies field A, other fields will become undefined.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>To reset a single field, you should use setValue method or carry all field values when resetting&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reset&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">getValues&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">price&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">111&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Note: There is setValue but no setValues, so to implement multiple values with setValue, you need to handle it yourself through iteration&lt;/strong>&lt;/p>
&lt;h5 id="shoulddirty">shouldDirty&lt;/h5>&lt;p>The second parameter in setValue has shouldDirty, indicating whether to set the dirty state. When set to true, the form will check the dirty state to determine updates to dirtyFields. For example, if there&amp;rsquo;s no change compared to defaultValues, dirtyFields will have values.&lt;/p>
&lt;p>&lt;strong>Note: After reset, defaultValues may be updated to the newly set values.&lt;/strong>&lt;/p>
&lt;h3 id="valueasnumbervalueasdate">
&lt;a class="heading-anchor-link" href="#valueasnumbervalueasdate">valueAsNumber/valueAsDate&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="valueasnumbervalueasdate"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{...&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;quantity&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// valueAsNumber: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">})}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#34;quantity&amp;#34;: &amp;#34;2121212121&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#34;quantity&amp;#34;: 2121212121
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If using Controller approach, you need to implement it yourself, such as &lt;code>field.onChange(Number(value))&lt;/code>&lt;/p>
&lt;h3 id="dirtyfields">
&lt;a class="heading-anchor-link" href="#dirtyfields">dirtyFields&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dirtyfields"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>formState contains dirtyFields, indicating which fields in the current form have been modified. Use this property when you need to determine which form fields have been changed.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="sb">```shell
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> const {isDirty, dirtyFields} = useFormState({
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> control
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> });
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="official-recommendations---practice">
&lt;a class="heading-anchor-link" href="#official-recommendations---practice">Official Recommendations - Practice👊&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="official-recommendations---practice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="transformparse">
&lt;a class="heading-anchor-link" href="#transformparse">transform/parse&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="transformparse"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;a href="https://react-hook-form.com/advanced-usage" target="_blank" rel="noopener">transform and parse&lt;/a> mentioned in react-hook-form are just practices, not built-in functionality.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-05-13-225028.jpeg"
alt="https://static.1991421.cn/2025/2025-05-13-225028.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">form_fields&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;price&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">transform&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">output&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">v&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="nx">v&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;num&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;quantity&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">form_fields&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Form&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Item&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Controller&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{({&lt;/span>&lt;span class="nx">field&lt;/span>&lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Input&lt;/span> &lt;span class="p">{...&lt;/span>&lt;span class="nx">field&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onChange&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">v&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">field&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onChange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">transform&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">transform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">output&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">control&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Form.Item&amp;gt;)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="integration-with-validation-libraries-like-yup">
&lt;a class="heading-anchor-link" href="#integration-with-validation-libraries-like-yup">Integration with validation libraries like yup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="integration-with-validation-libraries-like-yup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Common libraries include &lt;a href="https://www.npmjs.com/package/yup" target="_blank" rel="noopener">yup&lt;/a>/&lt;a href="https://www.npmjs.com/package/joi" target="_blank" rel="noopener">Joi&lt;/a>/&lt;a href="Superstruct">Superstruct&lt;/a>/&lt;a href="https://www.npmjs.com/package/zod" target="_blank" rel="noopener">zod&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">schema&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">yup&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">object&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">shape&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">price&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">yup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">number&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">required&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">quantity&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">yup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">number&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">max&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">required&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">required&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">formProps&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useForm&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolver&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">yupResolver&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">schema&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: Validation triggering still depends on the mode setting.&lt;/p>
&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common Issues❓&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="setvalue-with-null-values">
&lt;a class="heading-anchor-link" href="#setvalue-with-null-values">setValue with null values&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setvalue-with-null-values"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When setting values to null/undefined, controlled form components may not render updates properly.&lt;/p>
&lt;p>&lt;strong>This is due to React&amp;rsquo;s controlled component handling mechanism, not special handling by hook-form.&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;price&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">shouldDirty&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">inputValue&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setInputValue&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="nx">_0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">label&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">span&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">Name&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">span&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">inputValue&lt;/span>&lt;span class="p">}/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">label&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setInputValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Set&lt;/span> &lt;span class="nx">to&lt;/span> &lt;span class="nx">empty&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="solution">Solution&lt;/h4>&lt;p>For example, handle it during rendering like this.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">inputValue&lt;/span>&lt;span class="o">??&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">}/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="source-code">
&lt;a class="heading-anchor-link" href="#source-code">Source Code👀&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="source-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here we examine some parts of hook-form&amp;rsquo;s source code to answer certain questions.&lt;/p>
&lt;h3 id="package-dependencies---zero-dependency">
&lt;a class="heading-anchor-link" href="#package-dependencies---zero-dependency">Package dependencies - zero dependency&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-dependencies---zero-dependency"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>React-hook-form has no dependencies, only peerDependencies. So can any React project use react-hook-form normally???&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;peerDependencies&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^16.8.0 || ^17 || ^18 || ^19&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>For example, the WeChat mini-program development framework &lt;a href="https://github.com/NervJS/taro" target="_blank" rel="noopener">taro&lt;/a> uses React for development. Can react-hook-form be used for form validation if needed?&lt;/strong>&lt;/p>
&lt;p>Answer: &lt;code>YES&lt;/code>.&lt;/p>
&lt;p>Sample Taro mini-program code, using &lt;a href="https://github.com/jd-opensource/taro-ui" target="_blank" rel="noopener">taro-ui&lt;/a> for UI form components:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">control&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useForm&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;onChange&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Form&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Controller&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">control&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{({&lt;/span> &lt;span class="nx">field&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">AtInput&lt;/span> &lt;span class="p">{...&lt;/span>&lt;span class="nx">field&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="nx">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Name&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Controller&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">control&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{({&lt;/span> &lt;span class="nx">field&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">AtInput&lt;/span> &lt;span class="p">{...&lt;/span>&lt;span class="nx">field&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="nx">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;Test Input&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Form&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="resolver-principle">
&lt;a class="heading-anchor-link" href="#resolver-principle">resolver principle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resolver-principle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>React hook form can integrate with different schema validation libraries because there&amp;rsquo;s a separate package - &lt;a href="https://www.npmjs.com/package/@hookform/resolvers" target="_blank" rel="noopener">@hookform/resolvers&lt;/a> that provides adapter support for various libraries.&lt;/li>
&lt;/ol>
&lt;img src="https://static.1991421.cn/2025/2025-05-14-111141.jpeg" alt="https://static.1991421.cn/2025/2025-05-14-111141.jpeg" style="zoom:50%;" />
2. The resolver validates based on formState values combined with the schema, then returns error information to the form in the agreed format.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">values&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">errors&lt;/span>: &lt;span class="kt">toNestErrors&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parseErrorSchema&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">!&lt;/span>&lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">shouldUseNativeValidation&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">criteriaMode&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;all&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="good-performance">
&lt;a class="heading-anchor-link" href="#good-performance">Good performance?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="good-performance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Avoid unnecessary re-renders.&lt;/li>
&lt;li>Use native form registration mechanism + ref management.&lt;/li>
&lt;li>&lt;code>Controller&lt;/code> implements minimal rendering for controlled components.&lt;/li>
&lt;li>Destructure state as needed, rather than reading everything at once.&lt;/li>
&lt;/ol>
&lt;h3 id="usewatch-principle">
&lt;a class="heading-anchor-link" href="#usewatch-principle">useWatch principle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usewatch-principle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Essentially calls form.control&amp;rsquo;s subscribe method in the hook&amp;rsquo;s effect to monitor form value changes. Once values change, it updates the value object.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">useWatch&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">TFieldValues&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">FieldValues&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">props&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="nx">UseWatchProps&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">TFieldValues&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">useEffect&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_subscribe&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">_name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">InternalFieldName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">callback&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">formState&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">!&lt;/span>&lt;span class="nx">disabled&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">updateValue&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">generateWatchOutput&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">InternalFieldName&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">InternalFieldName&lt;/span>&lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_names&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">formState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">values&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">control&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_formValues&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_defaultValue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">control&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">disabled&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">exact&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">updateValue&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">useState&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_getWatch&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">InternalFieldName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">defaultValue&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">DeepPartialSkipArrayKey&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">TFieldValues&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="additional-information">
&lt;a class="heading-anchor-link" href="#additional-information">Additional Information🔗&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="additional-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="when-was-react-hook-form-released">
&lt;a class="heading-anchor-link" href="#when-was-react-hook-form-released">When was react-hook-form released?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-was-react-hook-form-released"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Research shows that react-hook-form released its first version on &lt;a href="https://github.com/react-hook-form/react-hook-form/graphs/contributors" target="_blank" rel="noopener">Mar 3, 2019&lt;/a> and is currently actively maintained.&lt;/p>
&lt;h3 id="package-size">
&lt;a class="heading-anchor-link" href="#package-size">Package size&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-size"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Current version: &lt;code>v7.56.3&lt;/code>, with GZIP compressed package size of approximately &lt;code>11KB&lt;/code>.&lt;/p>
&lt;h3 id="support-for-third-party-ui-form-components">
&lt;a class="heading-anchor-link" href="#support-for-third-party-ui-form-components">Support for third-party UI form components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="support-for-third-party-ui-form-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Such as &lt;a href="https://tea-design.github.io/component" target="_blank" rel="noopener">tea-component&lt;/a>, &lt;a href="https://mui.com/material-ui/getting-started/" target="_blank" rel="noopener">mui&lt;/a>, &lt;a href="https://www.npmjs.com/package/antd" target="_blank" rel="noopener">antd&lt;/a>.&lt;/p>
&lt;h3 id="other-form-solution-options">
&lt;a class="heading-anchor-link" href="#other-form-solution-options">Other form solution options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-form-solution-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;a href="https://github.com/final-form/final-form" target="_blank" rel="noopener">final-form&lt;/a>,&lt;a href="https://github.com/final-form/react-final-form" target="_blank" rel="noopener">react-final-form&lt;/a>,&lt;a href="https://github.com/final-form/react-final-form-hooks" target="_blank" rel="noopener">react-final-form-hooks&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jaredpalmer/formik" target="_blank" rel="noopener">formik&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="related-links">
&lt;a class="heading-anchor-link" href="#related-links">Related links&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-links"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;a href="https://react-hook-form.com/" target="_blank" rel="noopener">https://react-hook-form.com/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/orgs/react-hook-form/discussions/2704" target="_blank" rel="noopener">https://github.com/orgs/react-hook-form/discussions/2704&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.reddit.com/r/react/comments/1ed4sdj/controller_wrapper_vs_register_utility_function/" target="_blank" rel="noopener">https://www.reddit.com/r/react/comments/1ed4sdj/controller_wrapper_vs_register_utility_function/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://npmtrends.com/formik-vs-react-final-form-vs-react-hook-form" target="_blank" rel="noopener">https://npmtrends.com/formik-vs-react-final-form-vs-react-hook-form&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/react-hook-form/react-hook-form/pull/4825" target="_blank" rel="noopener">https://github.com/react-hook-form/react-hook-form/pull/4825&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.cn/post/7423261843933675558" target="_blank" rel="noopener">https://juejin.cn/post/7423261843933675558&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React Hook Form provides an excellent solution for form management in React applications, offering performance optimizations, flexibility, and a clean API. Its zero-dependency approach and small bundle size make it suitable for various projects, from web applications to mini-programs. By understanding its core concepts like validation modes, field registration, and state management, developers can build robust and efficient forms that provide great user experiences.&lt;/p></description></item><item><title>Resumable Transfers with ssh2-sftp-client</title><link>https://en.1991421.cn/2025/03/21/17038235/</link><pubDate>Fri, 21 Mar 2025 10:46:13 +0800</pubDate><guid>https://en.1991421.cn/2025/03/21/17038235/</guid><description>&lt;blockquote>
&lt;p>I’ve used &lt;code>ssh2-sftp-client&lt;/code> to implement terminal-side upload/download. Because it supports streams, I implemented chunked uploads/downloads with progress bars. But networks are unreliable and transfers can be interrupted, so resumable transfer is needed. Looking into the implementation, I found that &lt;code>ssh2-sftp-client&lt;/code> can support resuming.&lt;/p>
&lt;/blockquote>
&lt;h2 id="putget-methods">
&lt;a class="heading-anchor-link" href="#putget-methods">put/get Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="putget-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The third parameter to &lt;code>put&lt;/code>/&lt;code>get&lt;/code> is &lt;code>options&lt;/code>. It includes &lt;code>writeStreamOptions&lt;/code>/&lt;code>readStreamOptions&lt;/code>. Inspecting their definitions shows a &lt;code>start&lt;/code> parameter, which indicates the byte offset to start from.&lt;/p>
&lt;p>With &lt;code>start&lt;/code>, resumable transfer becomes straightforward.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">ReadStreamOptions&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">ReadableOptions&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">flags?&lt;/span>: &lt;span class="kt">OpenMode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mode?&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">start?&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">end?&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClose?&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">handle?&lt;/span>: &lt;span class="kt">Buffer&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">WriteStreamOptions&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">WritableOptions&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">flags?&lt;/span>: &lt;span class="kt">OpenMode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mode?&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">start?&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClose?&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">handle?&lt;/span>: &lt;span class="kt">Buffer&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoding?&lt;/span>: &lt;span class="kt">BufferEncoding&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conceptual-implementation">
&lt;a class="heading-anchor-link" href="#conceptual-implementation">Conceptual Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conceptual-implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Take upload as an example. After a successful transfer segment, record the last transferred byte offset on the client. If the network drops, the next attempt sets &lt;code>start&lt;/code> to that offset and continues. In essence, resumable transfer is similar to chunked transfer.&lt;/p></description></item><item><title>Next.js Project Build and Release Speed Optimization</title><link>https://en.1991421.cn/2025/03/20/nextjs/</link><pubDate>Thu, 20 Mar 2025 18:28:38 +0800</pubDate><guid>https://en.1991421.cn/2025/03/20/nextjs/</guid><description>&lt;blockquote>
&lt;p>Recently worked on a project using Next.js, the entire build and release time consumption was 12min, or longer. Looking at the entire build process, found there were optimization points, marking this here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="description">
&lt;a class="heading-anchor-link" href="#description">Description&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="description"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>CD pipeline uses Tencent Coding.&lt;/p>
&lt;h2 id="pre-optimization-logic">
&lt;a class="heading-anchor-link" href="#pre-optimization-logic">Pre-optimization Logic&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pre-optimization-logic"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Pipeline directly goes to docker build after pulling, dockerfile has following steps&lt;/p>
&lt;ol>
&lt;li>Builder intermediate process, npm install packages&lt;/li>
&lt;li>next build, package frontend resources&lt;/li>
&lt;li>run process, install packages, next start project&lt;/li>
&lt;/ol>
&lt;h2 id="after-optimization">
&lt;a class="heading-anchor-link" href="#after-optimization">After Optimization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="after-optimization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First about time, average 6min, currently fastest 5min.&lt;/p>
&lt;ol>
&lt;li>CI pipeline loads custom image environment for build, here using nodejs image&lt;/li>
&lt;li>Mount /root/workspace/.next/cache and npm cache&lt;/li>
&lt;li>Package under custom image, and choose next standalone mode&lt;/li>
&lt;li>docker build image doesn&amp;rsquo;t need multiple steps, only CP all next resources into directory, node server.js start&lt;/li>
&lt;/ol>
&lt;p>Note: After optimization, packaged image is 300MB, before optimization was 2GB.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Minimize build time as much as possible, thereby improving delivery speed.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://vwood.xyz/archive/89b4cf53-b249-4055-935b-07fcf5abf4bf" target="_blank" rel="noopener">https://vwood.xyz/archive/89b4cf53-b249-4055-935b-07fcf5abf4bf&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Once Handling in EventEmitter</title><link>https://en.1991421.cn/2025/03/18/eventemitteronce/</link><pubDate>Tue, 18 Mar 2025 15:48:53 +0800</pubDate><guid>https://en.1991421.cn/2025/03/18/eventemitteronce/</guid><description>&lt;h2 id="eventemitter-initial-version">
&lt;a class="heading-anchor-link" href="#eventemitter-initial-version">EventEmitter Initial Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eventemitter-initial-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">EventEmitter&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">constructor&lt;/span>&lt;span class="p">(){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">]){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">=&amp;gt;{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">listener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">off&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">]){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="o">!==-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">splice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">once&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">onceListener&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)=&amp;gt;{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">listener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">off&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">onceListener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">onceListener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="testing">
&lt;a class="heading-anchor-link" href="#testing">Testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">eventEmitter&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">EventEmitter&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">listener&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)=&amp;gt;{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">eventEmitter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">once&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">eventEmitter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">off&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">eventEmitter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test&amp;#39;&lt;/span>&lt;span class="p">,{&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When executing the above code, you&amp;rsquo;ll find that the test event was triggered once, but it should not execute because it was turned off. The solution is as follows.&lt;/p>
&lt;h2 id="once-failure">
&lt;a class="heading-anchor-link" href="#once-failure">Once Failure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="once-failure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">off&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">]){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">findIndex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">l&lt;/span>&lt;span class="p">=&amp;gt;&lt;/span>&lt;span class="nx">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="o">===&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="nx">l&lt;/span>&lt;span class="o">===&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="o">!==-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">events&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">splice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">once&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">onceListener&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)=&amp;gt;{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">listener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">off&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">onceListener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onceListener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">listener&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">onceListener&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Read more source code&lt;/p></description></item><item><title>Loading node_modules Resources in VSC WebView</title><link>https://en.1991421.cn/2025/03/17/vsc-webviewnode-modules/</link><pubDate>Mon, 17 Mar 2025 18:50:52 +0800</pubDate><guid>https://en.1991421.cn/2025/03/17/vsc-webviewnode-modules/</guid><description>&lt;blockquote>
&lt;p>Recently, while developing the gitlink plugin, I encountered an issue loading node_modules resources in WebView. Although CDN resources could be used directly, loading local resources is faster. Here&amp;rsquo;s how to solve the loading of node_modules resources.&lt;/p>
&lt;/blockquote>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Install npm dependency&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install @highlightjs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that this should be a dependency, not a dev dependency.&lt;/p>
&lt;ol start="2">
&lt;li>Configure localResourceRoots in webview&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">panel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">vscode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createWebviewPanel&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;codeImage&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Code Snippet Image&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">vscode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ViewColumn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Beside&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">enableScripts&lt;/span>: &lt;span class="kt">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">retainContextWhenHidden&lt;/span>: &lt;span class="kt">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">localResourceRoots&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">vscode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Uri&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">joinPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">extensionContext&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">extensionUri&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, we use extensionContext.extensionUri as localResourceRoots, allowing us to access node_modules resources within the extension.&lt;/p>
&lt;ol start="3">
&lt;li>Load resources in webview&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;node_modules/@highlightjs/cdn-assets/styles/github-dark.min.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Please note that relative path syntax is preferred over absolute path syntax. Absolute paths are not supported, but web addresses are.&lt;/p>
&lt;ol start="4">
&lt;li>Handle HTML href paths&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">readHtml&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">htmlPath&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">panel&lt;/span>: &lt;span class="kt">vscode.WebviewPanel&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">htmlPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="err">/%CSP_SOURCE%/gu, panel.webview.cspSource)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">/(src|href)=&amp;#34;([^&amp;#34;]*)&amp;#34;/gu,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">src&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="kr">type&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">=&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">panel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">webview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">asWebviewUri&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">vscode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Uri&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">htmlPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;../../../&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">src&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">&amp;#34;`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_panel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">webview&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">html&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_getHtmlContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">code&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">language&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">htmlTemplate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">readHtml&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_extensionContext&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">extensionPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;resources/webview/index.html&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_panel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is how I handle it - processing all resources in the HTML based on the HTML&amp;rsquo;s location to obtain the path relative to the extension for node_modules resources.&lt;/p>
&lt;h2 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Don&amp;rsquo;t add the node_modules folder in the .vscodeignore file. Otherwise, the package won&amp;rsquo;t include node_modules.&lt;/li>
&lt;/ul>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Following these steps solves the issue of loading node_modules resources in WebView. When updates are needed in the future, run the npm update and repackage the extension.&lt;/p></description></item><item><title>VSCode Extension Internationalization Support</title><link>https://en.1991421.cn/2025/03/14/vscode-extension-i18n/</link><pubDate>Fri, 14 Mar 2025 16:05:42 +0800</pubDate><guid>https://en.1991421.cn/2025/03/14/vscode-extension-i18n/</guid><description>&lt;blockquote>
&lt;p>Recently, I needed to implement internationalization for my VSCode extension. Since the official documentation on this topic was sparse, with only an example provided, I&amp;rsquo;ll summarize how to implement internationalization here.&lt;/p>
&lt;/blockquote>
&lt;p>Internationalization can be divided into two categories: menu internationalization and extension content internationalization, such as prompt messages.&lt;/p>
&lt;h2 id="menu-internationalization">
&lt;a class="heading-anchor-link" href="#menu-internationalization">Menu Internationalization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="menu-internationalization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="configure-packagejson">
&lt;a class="heading-anchor-link" href="#configure-packagejson">Configure package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configure-packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;contributes&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;commands&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gitlink.openInGitHub&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;%command.openInGitHub.title%&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the title uses a key format, and the key needs to be wrapped with %.&lt;/p>
&lt;h3 id="internationalization-json-files-in-project-root">
&lt;a class="heading-anchor-link" href="#internationalization-json-files-in-project-root">Internationalization JSON Files in Project Root&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="internationalization-json-files-in-project-root"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Below are two language files, default English and Simplified Chinese. If other languages are needed, corresponding JSON files can be added.&lt;/p>
&lt;ul>
&lt;li>package.nls.json&lt;/li>
&lt;li>package.nls.zh-cn.json&lt;/li>
&lt;/ul>
&lt;p>The content format is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;command.openInGitHub.title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Open in GitHub&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="extension-content-internationalization">
&lt;a class="heading-anchor-link" href="#extension-content-internationalization">Extension Content Internationalization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension-content-internationalization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="create-l10n-folder">
&lt;a class="heading-anchor-link" href="#create-l10n-folder">Create l10n Folder&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-l10n-folder"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Create an l10n folder in the project root directory.&lt;/p>
&lt;h3 id="configure-packagejson-1">
&lt;a class="heading-anchor-link" href="#configure-packagejson-1">Configure package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configure-packagejson-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;l10n&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./l10n&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="execute-resource-extraction-command">
&lt;a class="heading-anchor-link" href="#execute-resource-extraction-command">Execute Resource Extraction Command&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="execute-resource-extraction-command"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Execute the following command to extract all strings from the src directory to form internationalization JSON files.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npx @vscode/l10n-dev &lt;span class="nb">export&lt;/span> --outDir ./l10n ./src
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that files need to use the following format to be correctly extracted.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">vscode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">l10n&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">t&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Congratulations, your extension &amp;#34;GitLink&amp;#34; is now active!&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="create-target-language-json-files">
&lt;a class="heading-anchor-link" href="#create-target-language-json-files">Create Target Language JSON Files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-target-language-json-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-14-161959.jpeg"
alt="https://static.1991421.cn/2025/2025-03-14-161959.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="test">
&lt;a class="heading-anchor-link" href="#test">Test&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="test"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the internationalization configuration complete, try changing VSCode&amp;rsquo;s language settings to experience different languages during operation.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/microsoft/vscode-extension-samples/tree/main/l10n-sample" target="_blank" rel="noopener">https://github.com/microsoft/vscode-extension-samples/tree/main/l10n-sample&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>The Host Field in HTTP Request Headers</title><link>https://en.1991421.cn/2025/03/13/httphost/</link><pubDate>Thu, 13 Mar 2025 18:08:11 +0800</pubDate><guid>https://en.1991421.cn/2025/03/13/httphost/</guid><description>&lt;blockquote>
&lt;p>Recently came across a frontend question: &amp;ldquo;Is the Host field necessary when sending HTTP request headers?&amp;rdquo; I hadn&amp;rsquo;t paid much attention to this before, so I did some research and here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="is-the-host-field-necessary">
&lt;a class="heading-anchor-link" href="#is-the-host-field-necessary">Is the Host Field Necessary?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-the-host-field-necessary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Yes, it is necessary. For example, the same machine can have multiple hosts bound to the same port. In my blog server setup, I have domains like &lt;code>1991421.cn&lt;/code>, &lt;code>en.1991421.cn&lt;/code>, etc., all bound to the same server. Without the Host field, the server wouldn&amp;rsquo;t know which specific host you&amp;rsquo;re trying to access.&lt;/p>
&lt;h2 id="why-you-dont-see-host-in-chrome-network-tab">
&lt;a class="heading-anchor-link" href="#why-you-dont-see-host-in-chrome-network-tab">Why You Don&amp;rsquo;t See Host in Chrome Network Tab&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-you-dont-see-host-in-chrome-network-tab"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Starting with HTTP/2, the &lt;code>:authority&lt;/code> field is used instead of Host.&lt;/li>
&lt;li>If you examine HTTP/1.1 requests, you can see the Host field.&lt;/li>
&lt;/ol>
&lt;p>When we check on the server side, we can usually use &lt;code>req.host&lt;/code> normally because frameworks typically abstract away the differences between these fields.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-13-182352.jpeg"
alt="https://static.1991421.cn/2025/2025-03-13-182352.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-13-182439.jpeg"
alt="https://static.1991421.cn/2025/2025-03-13-182439.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-13-182541.jpeg"
alt="https://static.1991421.cn/2025/2025-03-13-182541.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="related-resources">
&lt;a class="heading-anchor-link" href="#related-resources">Related Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://superuser.com/questions/1687956/why-i-use-chrome-request-a-site-url-do-not-see-host-header" target="_blank" rel="noopener">Why I use chrome request a site URL do not see Host header?&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The Host header is indeed essential for modern web hosting where multiple domains share the same IP address and port. While HTTP/2 introduced the &lt;code>:authority&lt;/code> pseudo-header as a replacement, the underlying concept remains the same - identifying which specific host the client wants to communicate with on a shared server.&lt;/p></description></item><item><title>node-sass Discontinued Maintenance</title><link>https://en.1991421.cn/2025/03/10/node-sass/</link><pubDate>Mon, 10 Mar 2025 18:16:55 +0800</pubDate><guid>https://en.1991421.cn/2025/03/10/node-sass/</guid><description>&lt;blockquote>
&lt;p>Recently while looking at dependency package upgrades for an old project, I noticed that node-sass has been discontinued from maintenance, so I&amp;rsquo;m documenting this specifically.&lt;/p>
&lt;/blockquote>
&lt;h2 id="main-reasons">
&lt;a class="heading-anchor-link" href="#main-reasons">Main Reasons&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="main-reasons"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-insufficient-maintenance-manpower">
&lt;a class="heading-anchor-link" href="#1-insufficient-maintenance-manpower">1. Insufficient Maintenance Manpower&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-insufficient-maintenance-manpower"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>The recent maintenance team no longer has enough bandwidth (energy and time) to continue updating the project&lt;/li>
&lt;li>node-sass hasn&amp;rsquo;t had a new release version for a year and a half&lt;/li>
&lt;/ul>
&lt;h3 id="2-technical-evolution-process">
&lt;a class="heading-anchor-link" href="#2-technical-evolution-process">2. Technical Evolution Process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-technical-evolution-process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>node-sass was the first official Sass compiler available in the JavaScript ecosystem&lt;/li>
&lt;li>It&amp;rsquo;s essentially a Node.js wrapper for LibSass&lt;/li>
&lt;li>Now the official recommendation is to use Dart Sass as the primary implementation&lt;/li>
&lt;/ul>
&lt;h3 id="3-current-status">
&lt;a class="heading-anchor-link" href="#3-current-status">3. Current Status&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-current-status"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>The npm package has been marked as deprecated&lt;/li>
&lt;li>The GitHub repository has been archived to avoid confusion with active Sass repositories&lt;/li>
&lt;li>The underlying LibSass implementation:
&lt;ul>
&lt;li>Although deprecated, it hasn&amp;rsquo;t completely reached end-of-life&lt;/li>
&lt;li>Maintainer Marcel Greter still occasionally makes fixes&lt;/li>
&lt;li>There&amp;rsquo;s no officially supported way to use this implementation from Node.js&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="migration-recommendation">
&lt;a class="heading-anchor-link" href="#migration-recommendation">Migration Recommendation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="migration-recommendation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you&amp;rsquo;re currently still using node-sass, the official &lt;strong>strongly recommends&lt;/strong> migrating to the primary implementation Dart Sass.&lt;/p>
&lt;h2 id="historical-significance">
&lt;a class="heading-anchor-link" href="#historical-significance">Historical Significance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="historical-significance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This change marks an important transition in the Sass ecosystem:&lt;/p>
&lt;ol>
&lt;li>Initially developed in the Ruby community&lt;/li>
&lt;li>node-sass helped it take root in the JavaScript ecosystem&lt;/li>
&lt;li>Now shifting to Dart Sass as the primary implementation&lt;/li>
&lt;/ol>
&lt;h2 id="reference-sources">
&lt;a class="heading-anchor-link" href="#reference-sources">Reference Sources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-sources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://sass-lang.com/blog/node-sass-is-end-of-life/" target="_blank" rel="noopener">Node Sass is end-of-life&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cloudflare Security Check</title><link>https://en.1991421.cn/2025/03/10/cloudfare/</link><pubDate>Mon, 10 Mar 2025 17:23:30 +0800</pubDate><guid>https://en.1991421.cn/2025/03/10/cloudfare/</guid><description>&lt;blockquote>
&lt;p>Recently, a friend in our group chat encountered an error while filling out a waitlist form on manus and thought it was a site issue. After investigating, I found it was actually a Cloudflare issue. Once the problem was identified, it was easy to solve. I used my proxy node to help fill out the form.
This small issue highlights how many people are unaware of Cloudflare&amp;rsquo;s security checks, so I&amp;rsquo;ll briefly summarize it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="cloudflare-security-check-results">
&lt;a class="heading-anchor-link" href="#cloudflare-security-check-results">Cloudflare Security Check Results&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cloudflare-security-check-results"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-10-172552.jpeg"
alt="https://static.1991421.cn/2025/2025-03-10-172552.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-10-173621.jpeg"
alt="https://static.1991421.cn/2025/2025-03-10-173621.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The screenshots above show Cloudflare&amp;rsquo;s security check page on the manus website. Figure 1 shows success, Figure 2 shows failure.&lt;/p>
&lt;p>For websites that use Cloudflare, when accessing them, Cloudflare performs security checks. If the check fails, it can prevent further operations, such as form submissions.&lt;/p>
&lt;h2 id="what-does-the-security-check-verify">
&lt;a class="heading-anchor-link" href="#what-does-the-security-check-verify">What Does the Security Check Verify?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-does-the-security-check-verify"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Basic security validation&lt;/li>
&lt;li>Bot detection&lt;/li>
&lt;li>Threat intelligence checks&lt;/li>
&lt;li>DDoS protection checks
And more. Simply put, issues like problematic IP addresses can also cause the check to fail.&lt;/li>
&lt;/ol>
&lt;p>Things to note:&lt;/p>
&lt;ul>
&lt;li>If you&amp;rsquo;re accessing from mainland China, you may need to use a proxy since many CF IPs are blocked by the Great Firewall&lt;/li>
&lt;li>Some normal proxies or VPNs might trigger CF&amp;rsquo;s security checks&lt;/li>
&lt;li>The strictness of the checks can be adjusted by the site owner in the CF control panel&lt;/li>
&lt;/ul>
&lt;h2 id="how-to-resolve-security-check-issues-as-a-user">
&lt;a class="heading-anchor-link" href="#how-to-resolve-security-check-issues-as-a-user">How to Resolve Security Check Issues as a User&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-resolve-security-check-issues-as-a-user"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Change proxy nodes. If you&amp;rsquo;re in China, you need to use a proxy because many CF IPs are blocked.&lt;/li>
&lt;/ol>
&lt;p>For example, in my Surge configuration, I have the following rule to route Cloudflare traffic through a specific proxy node:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">DOMAIN-SUFFIX,cloudflare.com,Final-select
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This provides a basic understanding of Cloudflare&amp;rsquo;s security checks. Since my personally hosted sites still need to serve users in China, I&amp;rsquo;m not considering using Cloudflare for now.&lt;/p></description></item><item><title>How to Use Firecrawl MCP in Cursor (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/03/09/cursorfirecrawl-mcp/</link><pubDate>Sun, 09 Mar 2025 09:53:53 +0800</pubDate><guid>https://en.1991421.cn/2025/03/09/cursorfirecrawl-mcp/</guid><description>&lt;h2 id="features">
&lt;a class="heading-anchor-link" href="#features">Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s first discuss what Firecrawl MCP can do.&lt;/p>
&lt;ul>
&lt;li>Supports scraping, crawling, searching, extraction, deep research, and batch scraping&lt;/li>
&lt;li>Web scraping with JS rendering&lt;/li>
&lt;li>URL discovery and crawling&lt;/li>
&lt;li>Web search with content extraction&lt;/li>
&lt;li>Automatic retries with exponential backoff&lt;/li>
&lt;li>Efficient batch processing with built-in rate limiting&lt;/li>
&lt;li>Credit usage monitoring for cloud API&lt;/li>
&lt;li>Comprehensive logging system&lt;/li>
&lt;li>Support for both cloud and self-hosted FireCrawl instances&lt;/li>
&lt;li>Mobile/desktop viewport support&lt;/li>
&lt;li>Smart content filtering with tag inclusion/exclusion&lt;/li>
&lt;/ul>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Configure the Firecrawl MCP Server startup command in Cursor.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">env &lt;span class="nv">FIRECRAWL_API_KEY&lt;/span>&lt;span class="o">=&lt;/span>your-api-key npx -y firecrawl-mcp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Windows&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmd /c &lt;span class="s2">&amp;#34;set FIRECRAWL_API_KEY=your-api-key &amp;amp;&amp;amp; npx -y firecrawl-mcp
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I only have a Mac, so Windows hasn&amp;rsquo;t been tested.&lt;/p>
&lt;h2 id="available-tools">
&lt;a class="heading-anchor-link" href="#available-tools">Available Tools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="available-tools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-09-100455.jpeg"
alt="https://static.1991421.cn/2025/2025-03-09-100455.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As you can see, Cursor has integrated all Firecrawl tools. Therefore, you only need to use natural language to communicate with Cursor and access all Firecrawl tools.&lt;/p>
&lt;h2 id="results">
&lt;a class="heading-anchor-link" href="#results">Results&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="results"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-09-100745.jpeg"
alt="https://static.1991421.cn/2025/2025-03-09-100745.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: The more detailed the prompt, the more accurately Firecrawl tools can be called and the more accurate information you can get. I just did some simple testing here.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The AI crawler Firecrawl is good so far. If you need to crawl web page data, consider using it.&lt;/p></description></item><item><title>GitLink for VSCode: Seamless Cross-Platform Code Sharing Experience</title><link>https://en.1991421.cn/2025/03/02/gitlink-for-vscode/</link><pubDate>Sun, 02 Mar 2025 10:53:31 +0800</pubDate><guid>https://en.1991421.cn/2025/03/02/gitlink-for-vscode/</guid><description>&lt;blockquote>
&lt;p>I frequently use the &lt;a href="https://plugins.jetbrains.com/plugin/8183-gitlink" target="_blank" rel="noopener">GitLink&lt;/a> plugin in JetBrains IDEs to copy Git web URLs for files with one click, which significantly streamlines code collaboration.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Increased VSCode usage at work recently revealed a lack of comparable extensions. To improve efficiency, I developed Beautiful GitLink, a VSCode extension that replicates and enhances this functionality.&lt;/p>
&lt;/blockquote>
&lt;h2 id="demo">
&lt;a class="heading-anchor-link" href="#demo">Demo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="demo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/418260403-7729ab71-81f8-4d20-bf41-dca3c0a82ded.gif"
alt="https://static.1991421.cn/2025/418260403-7729ab71-81f8-4d20-bf41-dca3c0a82ded.gif"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="features">
&lt;a class="heading-anchor-link" href="#features">Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Supports one-click copying of Git web URLs from both File Explorer and active editors&lt;/li>
&lt;li>Multiple copy formats: raw URL, Markdown link, or Markdown link with code snippet&lt;/li>
&lt;li>One-click browser access to Git web URLs&lt;/li>
&lt;li>Built-in templates for major Git platforms with options for custom templates&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-03-02-110100.jpeg"
alt="https://static.1991421.cn/2025/2025-03-02-110100.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="built-in-supported-git-platforms">
&lt;a class="heading-anchor-link" href="#built-in-supported-git-platforms">Built-in Supported Git Platforms&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="built-in-supported-git-platforms"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Currently supports these platforms, with custom platform configuration available:&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>GitHub&lt;/li>
&lt;li>GitLab&lt;/li>
&lt;li>Gitee&lt;/li>
&lt;li>Tencent Coding&lt;/li>
&lt;li>Tencent WorkerBee (Community Edition, Internal Network Version)&lt;/li>
&lt;/ul>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://marketplace.visualstudio.com/items?itemName=AlanHe.cn-alanhe-gitlink" target="_blank" rel="noopener">https://marketplace.visualstudio.com/items?itemName=AlanHe.cn-alanhe-gitlink&lt;/a>&lt;/p>
&lt;h2 id="settings-sync">
&lt;a class="heading-anchor-link" href="#settings-sync">Settings Sync&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="settings-sync"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>VSCode natively supports configuration syncing through GitHub account login. For Cursor IDE users, manual sync or third-party sync extensions are necessary.&lt;/p>
&lt;h2 id="data-security">
&lt;a class="heading-anchor-link" href="#data-security">Data Security&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="data-security"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>No server interactions&lt;/li>
&lt;li>Zero-user data collection&lt;/li>
&lt;/ul>
&lt;h2 id="final-notes">
&lt;a class="heading-anchor-link" href="#final-notes">Final Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Active maintenance and development continue. Please submit &lt;a href="https://github.com/alanhe421/vsc-extension-gitlink-issues/issues" target="_blank" rel="noopener">issues&lt;/a> for feedback.&lt;/p></description></item><item><title>Building Real-time Web Page Ranking Monitoring with GitHub Action</title><link>https://en.1991421.cn/2025/02/20/github-action/</link><pubDate>Thu, 20 Feb 2025 14:57:07 +0800</pubDate><guid>https://en.1991421.cn/2025/02/20/github-action/</guid><description>&lt;blockquote>
&lt;p>Recently, I had a requirement to monitor the ranking situation of a certain entry in a competition. Unfortunately, the event itself doesn&amp;rsquo;t have a ranking page, and the event data is paginated and unordered. Therefore, I thought of using GitHub Action to build a real-time web page ranking monitoring tool.&lt;/p>
&lt;/blockquote>
&lt;h2 id="implementation-approach">
&lt;a class="heading-anchor-link" href="#implementation-approach">Implementation Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Set up a GitHub Action schedule cron job, for example, execute every 10 minutes.&lt;/li>
&lt;li>Execute a script to request the web page&amp;rsquo;s API interface and get data information. Since I&amp;rsquo;m more familiar with JS, I use Node.js for implementation.&lt;/li>
&lt;li>Based on the data information, calculate ranking situations and process into needed data.&lt;/li>
&lt;li>Update to HTML based on the data.&lt;/li>
&lt;li>SSH connect to the server and upload HTML files.&lt;/li>
&lt;li>Meanwhile, send data to Telegram for instant IM notifications.&lt;/li>
&lt;/ol>
&lt;h2 id="final-deliverables">
&lt;a class="heading-anchor-link" href="#final-deliverables">Final Deliverables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-deliverables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>HTML page displaying ranking information, refreshed every 10 minutes&lt;/li>
&lt;li>Telegram bot sending ranking information&lt;/li>
&lt;/ol>
&lt;h2 id="github-action-usage-notes">
&lt;a class="heading-anchor-link" href="#github-action-usage-notes">GitHub Action Usage Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="github-action-usage-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>GitHub Action&amp;rsquo;s cron expressions currently &lt;code>do not support timezone settings&lt;/code>&lt;/li>
&lt;li>For individual accounts/organizations, non-public repositories have execution quotas - currently 2000 minutes. If exceeded, usage will be suspended
Quota details: &lt;a href="https://github.com/settings/billing/summary" target="_blank" rel="noopener">https://github.com/settings/billing/summary&lt;/a>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-02-20-153358.jpeg"
alt="https://static.1991421.cn/2025/2025-02-20-153358.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>GitHub Action execution environments include macOS, which can be configured if needed&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Building this entire system including development took just about an hour. I have to say GitHub Action is truly powerful, and you can create many creative solutions with it. The scheduled jobs and web scraping mentioned above are just the tip of the iceberg - there&amp;rsquo;s much more to explore in the future.&lt;/p></description></item><item><title>Web Dev — Show Search Button on iOS Keyboard</title><link>https://en.1991421.cn/2025/02/20/web-dev-ios-keyboard-search/</link><pubDate>Thu, 20 Feb 2025 14:04:21 +0800</pubDate><guid>https://en.1991421.cn/2025/02/20/web-dev-ios-keyboard-search/</guid><description>&lt;blockquote>
&lt;p>On mobile, focusing a web input field brings up the system keyboard. If you want a “Search” button to appear on the keyboard, some attributes are needed.&lt;/p>
&lt;/blockquote>
&lt;h2 id="input-typesearch">
&lt;a class="heading-anchor-link" href="#input-typesearch">input type=&amp;ldquo;search&amp;rdquo;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="input-typesearch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the following, iOS does not show a Search button in testing, while Android does.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;search&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="enterkeyhintsearch">
&lt;a class="heading-anchor-link" href="#enterkeyhintsearch">enterKeyHint=&amp;ldquo;search&amp;rdquo;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enterkeyhintsearch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the following, iOS does show the Search button. Android also displays it correctly.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;search&amp;#34;&lt;/span> &lt;span class="na">enterKeyHint&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;search&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="search-event-firing">
&lt;a class="heading-anchor-link" href="#search-event-firing">search event firing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="search-event-firing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>On Android, pressing the Search button fires the &lt;code>search&lt;/code> event. On iPhone, pressing Search only triggers the Enter event. So listen for Enter and check accordingly.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">el&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;search&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">el&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;enter&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">key&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;Enter&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Search&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above solves showing a Search button on the mobile keyboard for both iOS and Android when an input is focused.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use ESLint (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/02/19/ab80b61/</link><pubDate>Wed, 19 Feb 2025 12:35:31 +0800</pubDate><guid>https://en.1991421.cn/2025/02/19/ab80b61/</guid><description>&lt;h2 id="common-plugins">
&lt;a class="heading-anchor-link" href="#common-plugins">Common Plugins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-plugins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;eslint-config-tencent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;plugin:react/recommended&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;plugin:@typescript-eslint/recommended&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;plugin:react-hooks/recommended&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-links">
&lt;a class="heading-anchor-link" href="#related-links">Related Links&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-links"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.npmjs.com/package/eslint-config-tencent" target="_blank" rel="noopener">https://www.npmjs.com/package/eslint-config-tencent&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Webpack Plugin to Auto-Rename Variables</title><link>https://en.1991421.cn/2025/02/14/webpack/</link><pubDate>Fri, 14 Feb 2025 16:21:49 +0800</pubDate><guid>https://en.1991421.cn/2025/02/14/webpack/</guid><description>&lt;blockquote>
&lt;p>I previously built a webpack loader called domain-replace-loader to replace domains in frontend JS, e.g., swapping to a new domain or a global &lt;code>window&lt;/code> property, avoiding manual edits across repos. Sometimes the requirement isn’t about domains. A colleague recently asked to rename variables in code automatically. For example, rename &lt;code>T_PROJECT&lt;/code> to &lt;code>P_PROJECT&lt;/code>. I updated the plugin to support automatic variable renaming.&lt;/p>
&lt;/blockquote>
&lt;h2 id="approach">
&lt;a class="heading-anchor-link" href="#approach">Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The core is parsing code with esprima, walking tokens, and operating based on each token’s type.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-02-14-163225.jpeg"
alt="https://static.1991421.cn/2025/2025-02-14-163225.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As you can see, the token type for identifiers is &lt;code>Identifier&lt;/code>. So we just check whether the identifier name matches, and if it does, perform the replacement.&lt;/p>
&lt;p>Be mindful of performance overhead during replacement.&lt;/p>
&lt;h2 id="key-code">
&lt;a class="heading-anchor-link" href="#key-code">Key Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;Identifier&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">q&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reg&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">source&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">source&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With auto-renaming supported, you can build other useful features—for example, keyword masking. For masking, you’d need to handle not just &lt;code>Identifier&lt;/code> but also string literals.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://esprima.org/demo/parse.html" target="_blank" rel="noopener">https://esprima.org/demo/parse.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jquery/esprima" target="_blank" rel="noopener">https://github.com/jquery/esprima&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>npm Cache</title><link>https://en.1991421.cn/2025/02/13/f4eb9d1/</link><pubDate>Thu, 13 Feb 2025 17:36:48 +0800</pubDate><guid>https://en.1991421.cn/2025/02/13/f4eb9d1/</guid><description>&lt;blockquote>
&lt;p>When developing with npm, you may notice that if you delete the node_modules folder and then reinstall dependencies, the installation is very fast. This is because npm has a caching mechanism.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-is-npm-cache">
&lt;a class="heading-anchor-link" href="#what-is-npm-cache">What is npm Cache&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-npm-cache"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>npm cache is a mechanism where npm downloads dependency packages to a local cache directory during installation. The next time you install, if the dependency package already exists in the cache directory, npm retrieves it directly from the cache instead of downloading it again.&lt;/p>
&lt;h2 id="how-to-view-npm-cache">
&lt;a class="heading-anchor-link" href="#how-to-view-npm-cache">How to View npm Cache&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-view-npm-cache"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm config get cache
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm cache ls
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="cache-directories-for-different-package-managers">
&lt;a class="heading-anchor-link" href="#cache-directories-for-different-package-managers">Cache Directories for Different Package Managers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cache-directories-for-different-package-managers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Package Manager&lt;/th>
&lt;th style="text-align: left">Cache Directory&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Maven&lt;/td>
&lt;td style="text-align: left">/root/.m2/&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Gradle&lt;/td>
&lt;td style="text-align: left">/root/.gradle/&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">npm&lt;/td>
&lt;td style="text-align: left">/root/.npm/&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">composer&lt;/td>
&lt;td style="text-align: left">/root/.cache/composer/&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">yarn&lt;/td>
&lt;td style="text-align: left">/usr/local/share/.cache/yarn/&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Done.&lt;/p></description></item><item><title>How to Use OpenResty (Step-by-Step Guide)</title><link>https://en.1991421.cn/2025/02/07/openresty/</link><pubDate>Fri, 07 Feb 2025 21:16:44 +0800</pubDate><guid>https://en.1991421.cn/2025/02/07/openresty/</guid><description>&lt;blockquote>
&lt;p>Recently, I needed to modify the GZIP configuration for a web service. After checking the network, I noticed the response header showed &lt;code>server:openresty/1.21.4.1&lt;/code>, which revealed that the gateway was using OpenResty. After confirming with the backend team, I needed to modify the GZIP configuration, which required some understanding of OpenResty.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-is-openresty">
&lt;a class="heading-anchor-link" href="#what-is-openresty">What is OpenResty&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-openresty"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>OpenResty is a web server based on nginx that integrates nginx&amp;rsquo;s powerful performance with Lua&amp;rsquo;s dynamic capabilities.&lt;/p>
&lt;h2 id="modifying-gzip-configuration-in-openresty">
&lt;a class="heading-anchor-link" href="#modifying-gzip-configuration-in-openresty">Modifying GZIP Configuration in OpenResty&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="modifying-gzip-configuration-in-openresty"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>OpenResty&amp;rsquo;s GZIP configuration is similar to nginx&amp;rsquo;s configuration, primarily using the &lt;code>gzip&lt;/code> directive.&lt;/p>
&lt;p>nginx configuration path: &lt;code>/usr/local/openresty/nginx/conf/nginx.conf&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gzip on&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_min_length 256&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="testing-configuration">
&lt;a class="heading-anchor-link" href="#testing-configuration">Testing Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In practice, you should first test if the configuration is correct before restarting.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openresty -t
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="restarting-openresty">
&lt;a class="heading-anchor-link" href="#restarting-openresty">Restarting OpenResty&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="restarting-openresty"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openresty -s reload
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="checking-openresty-version">
&lt;a class="heading-anchor-link" href="#checking-openresty-version">Checking OpenResty Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="checking-openresty-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">openresty -v
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>So far, I&amp;rsquo;ve only worked with OpenResty&amp;rsquo;s GZIP configuration. I&amp;rsquo;ll add more when I encounter other configurations.&lt;/li>
&lt;li>As of now, I still prefer Caddy&amp;rsquo;s configuration approach - it&amp;rsquo;s more straightforward and clear.&lt;/li>
&lt;/ol>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/openresty/openresty" target="_blank" rel="noopener">https://github.com/openresty/openresty&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://openresty.org/en/installation.html" target="_blank" rel="noopener">https://openresty.org/en/installation.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Handling Browser Compatibility in Vite</title><link>https://en.1991421.cn/2025/02/07/vite/</link><pubDate>Fri, 07 Feb 2025 15:09:45 +0800</pubDate><guid>https://en.1991421.cn/2025/02/07/vite/</guid><description>&lt;blockquote>
&lt;p>Vite ships an official solution for legacy browser support.&lt;/p>
&lt;/blockquote>
&lt;h2 id="install-the-plugin">
&lt;a class="heading-anchor-link" href="#install-the-plugin">Install the Plugin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-the-plugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install @vitejs/plugin-legacy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="configure">
&lt;a class="heading-anchor-link" href="#configure">Configure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... other plugins
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">legacy&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">targets&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;defaults&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;chrome &amp;gt;= 55&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;safari &amp;gt;= 12&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;not IE 11&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;firefox &amp;gt;= 52&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The plugin simplifies compatibility—no need to fiddle with Browserslist or core-js directly.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Because polyfills are injected based on targets, expect longer build times.&lt;/li>
&lt;li>Avoid configuring core-js manually; the plugin handles it.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s all—quick and clean.&lt;/p></description></item><item><title>Detect and Remove Unused Dependencies in Projects</title><link>https://en.1991421.cn/2025/01/26/detect-remove-unused-deps/</link><pubDate>Sun, 26 Jan 2025 09:52:22 +0800</pubDate><guid>https://en.1991421.cn/2025/01/26/detect-remove-unused-deps/</guid><description>&lt;blockquote>
&lt;p>Recently when looking at some projects, I found many dependencies. After searching a few randomly, I discovered they weren&amp;rsquo;t being used and could be removed. However, checking and removing these packages one by one is too troublesome.
Based on this need, I wrote a small tool to efficiently detect and remove unused dependencies.&lt;/p>
&lt;/blockquote>
&lt;h2 id="stackerremove-unused-deps">
&lt;a class="heading-anchor-link" href="#stackerremove-unused-deps">@stacker/remove-unused-deps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="stackerremove-unused-deps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Usage: Execute the following command in the project root directory, interactively select unused dependencies, and press enter to uninstall and remove them.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npx @stacker/remove-unused-deps@latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="principle">
&lt;a class="heading-anchor-link" href="#principle">Principle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="principle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>depcheck: This tool is excellent mainly because it can detect unused dependencies and development dependencies in projects. However, it has the drawback of only outputting unused dependencies without actively uninstalling them. Therefore, it must be combined with other tools to complete the task.&lt;/li>
&lt;li>fs scans the project&amp;rsquo;s lock file to determine whether the package manager is yarn or npm&lt;/li>
&lt;li>Build an interactive command line that executes the corresponding uninstall command after the user selects the dependencies to be removed&lt;/li>
&lt;/ol>
&lt;h2 id="special-case-packages">
&lt;a class="heading-anchor-link" href="#special-case-packages">Special Case Packages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="special-case-packages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Some packages, although detected as unused, may not be unnecessary but are needed in the project. For example, if the project depends on package A and package A uses package B, but package B isn&amp;rsquo;t listed in the dependencies, manual user confirmation is still needed.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With this tool, it&amp;rsquo;s convenient to detect and remove unused dependencies in projects.
Removing unused packages helps clarify project dependencies and prevents developers from using packages just because they&amp;rsquo;re available, which can lead to multiple solution packages coexisting in the project.
Currently, this tool only supports yarn and npm; other package managers are not yet supported. However, as demand increases, this will be supported later.&lt;/p></description></item><item><title>Pipeline Optimization — Cache Directories</title><link>https://en.1991421.cn/2025/01/25/pipeline-cache-optimization/</link><pubDate>Sat, 25 Jan 2025 10:17:23 +0800</pubDate><guid>https://en.1991421.cn/2025/01/25/pipeline-cache-optimization/</guid><description>&lt;blockquote>
&lt;p>Continuous integration pipelines often rebuild frontend projects, and package installs happen constantly. If every job starts from scratch—fresh checkout, fresh &lt;code>npm install&lt;/code>—the feedback loop crawls. Caching pays off quickly.&lt;/p>
&lt;/blockquote>
&lt;h2 id="typical-case-cache-npm-on-the-host">
&lt;a class="heading-anchor-link" href="#typical-case-cache-npm-on-the-host">Typical Case: Cache npm on the Host&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typical-case-cache-npm-on-the-host"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2025/2025-01-25-102018.jpeg"
alt="https://static.1991421.cn/2025/2025-01-25-102018.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Scenario: the CI agent installs dependencies directly on the host with &lt;code>npm install&lt;/code>. Persist the host’s npm cache so subsequent runs reuse it. This doesn’t help Docker-based builds because installing inside the container doesn’t touch the host’s &lt;code>node_modules&lt;/code>.&lt;/p>
&lt;h2 id="docker-builds-mount-the-npm-cache">
&lt;a class="heading-anchor-link" href="#docker-builds-mount-the-npm-cache">Docker Builds: Mount the npm Cache&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docker-builds-mount-the-npm-cache"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">pipeline&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">agent&lt;/span> &lt;span class="n">any&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stages&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stage&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;npm cache&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">steps&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">script&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">docker&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">image&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;node:14&amp;#39;&lt;/span>&lt;span class="o">).&lt;/span>&lt;span class="na">inside&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;-v /root/.npm/:/root/.npm/&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sh&lt;/span> &lt;span class="s1">&amp;#39;npm install&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Scenario: the CI pipeline builds the frontend Docker image, and the &lt;code>npm install&lt;/code> step happens inside the container. Mount the host’s npm cache into the container to avoid redownloading packages.&lt;/p>
&lt;h2 id="timing-comparison">
&lt;a class="heading-anchor-link" href="#timing-comparison">Timing Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="timing-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Measure your pipeline both ways. In my tests, caching slashed repeated builds from minutes to seconds.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Whether it’s frontend or backend, look for caches that can be safely shared between runs to boost throughput.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://coding.net/help/docs/ci/configuration/cache.html" target="_blank" rel="noopener">https://coding.net/help/docs/ci/configuration/cache.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://coding.net/help/docs/ci/practice/custom-build-env.html" target="_blank" rel="noopener">https://coding.net/help/docs/ci/practice/custom-build-env.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>One-Click Open VSCode Projects with Alfred</title><link>https://en.1991421.cn/2025/01/23/alfredvscode/</link><pubDate>Thu, 23 Jan 2025 14:10:02 +0800</pubDate><guid>https://en.1991421.cn/2025/01/23/alfredvscode/</guid><description>&lt;blockquote>
&lt;p>Cursor(VSCode) has been used frequently for coding, but opening projects is inefficient. I wanted to create an Alfred Workflow for a one-click VSCode project opening. Research showed that VSCode stores recently opened projects, making this technically feasible.&lt;/p>
&lt;/blockquote>
&lt;h2 id="screenshots">
&lt;a class="heading-anchor-link" href="#screenshots">Screenshots&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="screenshots"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://github.com/alanhe421/alfred-workflows/blob/master/vscode-open-project/screenshots/screenshot1.gif?raw=true"
alt="https://github.com/alanhe421/alfred-workflows/blob/master/vscode-open-project/screenshots/screenshot1.gif?raw=true"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Download: &lt;a href="https://github.com/alanhe421/alfred-workflows/tree/master/vscode-open-project" target="_blank" rel="noopener">VSCode-Open Project&lt;/a>&lt;/p>
&lt;h2 id="implementation-approach">
&lt;a class="heading-anchor-link" href="#implementation-approach">Implementation Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="database-access">
&lt;a class="heading-anchor-link" href="#database-access">Database Access&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="database-access"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>VSCode/Cursor stores recent file/folder history in:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>VSCode
$HOME/Library/Application\ Support/Code/User/globalStorage/state.vscdb&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Cursor
$HOME/Library/Application\ Support/Cursor/User/globalStorage/state.vscdb&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Note: &lt;code>Application Support&lt;/code> contains spaces, requiring &lt;code>\&lt;/code> escaping in the command line.&lt;/p>
&lt;h3 id="database-query">
&lt;a class="heading-anchor-link" href="#database-query">Database Query&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="database-query"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use Ruby to query and extract recent project paths from &lt;code>history.recentlyOpenedPathsList&lt;/code> in ItemTable, parsing JSON data with jq.&lt;/p>
&lt;ol>
&lt;li>Mac has SQLite pre-installed for direct use.&lt;/li>
&lt;/ol>
&lt;h3 id="alfred-format-output">
&lt;a class="heading-anchor-link" href="#alfred-format-output">Alfred Format Output&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="alfred-format-output"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>With data extracted, the output follows the Alfred Script Filter format.&lt;/p>
&lt;h3 id="open-vscodecursor">
&lt;a class="heading-anchor-link" href="#open-vscodecursor">Open VSCode/Cursor&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-vscodecursor"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Multiple methods:&lt;/p>
&lt;ol>
&lt;li>URL Scheme&lt;/li>
&lt;li>Command line (&lt;code>vscode&lt;/code>, &lt;code>cursor&lt;/code>)&lt;/li>
&lt;/ol>
&lt;p>URL Scheme was used, and both VSCode and Cursor support it.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This workflow significantly simplifies opening VSCode/Cursor projects.&lt;/p></description></item><item><title>Is Deploying Node.js on Multi-Core Machines a Resource Waste</title><link>https://en.1991421.cn/2025/01/06/nodejs/</link><pubDate>Mon, 06 Jan 2025 15:49:56 +0800</pubDate><guid>https://en.1991421.cn/2025/01/06/nodejs/</guid><description>&lt;blockquote>
&lt;p>Our server-side project is deployed in a cluster, with each POD running a docker-nodejs application. POD configuration is 2 CPU cores, 16GB memory. Since Node.js is single-threaded, is there resource waste here? Let&amp;rsquo;s think about this issue.&lt;/p>
&lt;/blockquote>
&lt;h2 id="application-situation">
&lt;a class="heading-anchor-link" href="#application-situation">Application Situation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="application-situation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Multiple HTTP servers are started under app.js, each server listening on a different port.&lt;/li>
&lt;/ul>
&lt;p>Although multiple port services are started here, they still only occupy one process on the machine. Since Node.js is single-threaded, these multiple web services themselves also run in a single thread, and there is resource competition between them.&lt;/p>
&lt;h2 id="cpu-waste">
&lt;a class="heading-anchor-link" href="#cpu-waste">CPU Waste?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cpu-waste"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Looking at the actual situation of the production project, CPU usage is very low, reaching only 18% at maximum.&lt;/p>
&lt;h2 id="is-multi-core-usage-possible">
&lt;a class="heading-anchor-link" href="#is-multi-core-usage-possible">Is Multi-Core Usage Possible?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-multi-core-usage-possible"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Node.js applications run under a single process, but Node.js has some commands like &lt;code>child_process&lt;/code>, &lt;code>cluster&lt;/code>, &lt;code>worker_threads&lt;/code>. These commands can create child processes, and child processes can run in different threads, thus utilizing multiple cores. Therefore, there is a possibility of multi-core usage.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Single-threaded Node.js applications on multi-core machines have the possibility of resource waste. But to be safe, it should be based on actual load conditions&lt;/li>
&lt;li>Considering that Node.js has the possibility of multi-process usage, multi-core is also fine.&lt;/li>
&lt;/ul></description></item><item><title>Command Line Git Operations Repeatedly Prompt for Username and Password</title><link>https://en.1991421.cn/2025/01/06/git-password-prompt-repeat/</link><pubDate>Mon, 06 Jan 2025 15:02:31 +0800</pubDate><guid>https://en.1991421.cn/2025/01/06/git-password-prompt-repeat/</guid><description>&lt;blockquote>
&lt;p>When executing git pull/push operations in the terminal, it keeps prompting for username and password, even though I&amp;rsquo;ve already entered them before. Here&amp;rsquo;s how to solve this issue.&lt;/p>
&lt;/blockquote>
&lt;h2 id="git-prompting-for-username-and-password">
&lt;a class="heading-anchor-link" href="#git-prompting-for-username-and-password">Git Prompting for Username and Password&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="git-prompting-for-username-and-password"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">Username &lt;span class="k">for&lt;/span> &lt;span class="s1">&amp;#39;https://git.xxx.com&amp;#39;&lt;/span>: Password &lt;span class="k">for&lt;/span> &lt;span class="s1">&amp;#39;https://git.xxx.com&amp;#39;&lt;/span>:
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Open the terminal and execute &lt;code>git config --global credential.helper store&lt;/code> command to store credentials locally.&lt;/li>
&lt;li>Execute the &lt;code>git pull/push&lt;/code> operation, enter username and password once, and it won&amp;rsquo;t prompt again for subsequent operations.&lt;/li>
&lt;/ol>
&lt;h3 id="command-scope-explanation">
&lt;a class="heading-anchor-link" href="#command-scope-explanation">Command Scope Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="command-scope-explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>When using the &lt;code>--global&lt;/code> parameter, the setting affects all Git repositories for the current user. The configuration is saved in the &lt;code>~/.gitconfig&lt;/code> file in the user&amp;rsquo;s home directory.&lt;/li>
&lt;li>If you want it to only affect the current repository, remove the &lt;code>--global&lt;/code> parameter and execute &lt;code>git config credential.helper store&lt;/code>. The configuration will be saved in the &lt;code>.git/config&lt;/code> file of the current repository.&lt;/li>
&lt;/ul>
&lt;h3 id="alternative-solutions">
&lt;a class="heading-anchor-link" href="#alternative-solutions">Alternative Solutions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="alternative-solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If the above operation doesn&amp;rsquo;t work, try the following steps:&lt;/p>
&lt;ol>
&lt;li>First, clear existing credential configuration:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git config --global --unset credential.helper
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Reset credential storage:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git config --global credential.helper store
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Git is a frequently used tool, so it&amp;rsquo;s important to document common issues promptly for future reference.&lt;/p></description></item><item><title>peerDependencies Explained (Simple Guide)</title><link>https://en.1991421.cn/2025/01/02/peerdependencies/</link><pubDate>Thu, 02 Jan 2025 11:05:26 +0800</pubDate><guid>https://en.1991421.cn/2025/01/02/peerdependencies/</guid><description>&lt;blockquote>
&lt;p>Besides dependency and devDependency in package.json, there&amp;rsquo;s also peerDependency. Let&amp;rsquo;s resolve a real-world error to understand how it works.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error-message">
&lt;a class="heading-anchor-link" href="#error-message">Error Message&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-message"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm ERR! code ERESOLVE
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! ERESOLVE could not resolve
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! While resolving: a@0.0.1-beta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! Found: ssh2@1.15.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! node_modules/ssh2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! ssh2@&amp;#34;1.15.0&amp;#34; from the root project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! ssh2@&amp;#34;^1.12.0&amp;#34; from ssh2-sftp-client@9.1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! node_modules/ssh2-sftp-client
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! ssh2-sftp-client@&amp;#34;^9.1.0&amp;#34; from the root project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! Could not resolve dependency:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! peer ssh2@&amp;#34;1.16.0&amp;#34; from a@0.0.1-beta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! packages/a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! a@0.0.1-beta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! node_modules/a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! workspace packages/a from the root project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! Conflicting peer dependency: ssh2@1.16.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! node_modules/ssh2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! peer ssh2@&amp;#34;1.16.0&amp;#34; from a@0.0.1-beta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! packages/a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! a@0.0.1-beta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! node_modules/a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! workspace packages/a from the root project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! Fix the upstream dependency conflict, or retry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! this command with --force or --legacy-peer-deps
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! For a full report see:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! /Users/alanhe/.npm/_logs/2025-01-02T02_53_13_141Z-eresolve-report.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! A complete log of this run can be found in:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! /Users/alanhe/.npm/_logs/2025-01-02T02_53_13_141Z-debug-0.log
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="error-analysis">
&lt;a class="heading-anchor-link" href="#error-analysis">Error Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The project&amp;rsquo;s direct dependency pins ssh2 at version 1.15.0, while package a lists &lt;a href="mailto:ssh2@1.16.0">ssh2@1.16.0&lt;/a> in its peerDependency field.&lt;/p>
&lt;p>Because peerDependencies declare which version the consuming project must install, npm expects us to have &lt;a href="mailto:ssh2@1.16.0">ssh2@1.16.0&lt;/a>. With 1.15.0 installed, it throws a conflict.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Once you know the root cause, the fix is simple: upgrade ssh2 in the main project to the required version.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>PeerDependencies let a package specify the acceptable version range of another dependency in the consuming project. When the consuming project also depends on it, that version must satisfy the peer constraint.&lt;/li>
&lt;li>Declaring something in peerDependencies doesn&amp;rsquo;t install it automatically if the main project doesn&amp;rsquo;t list it in dependencies. In current npm versions (such as v9), it only gets installed when another package depends on it.&lt;/li>
&lt;/ol></description></item><item><title>Click-to-Component Source Code Analysis</title><link>https://en.1991421.cn/2024/12/31/click-to-component/</link><pubDate>Tue, 31 Dec 2024 16:08:48 +0800</pubDate><guid>https://en.1991421.cn/2024/12/31/click-to-component/</guid><description>&lt;blockquote>
&lt;p>Click-to-component is a React component that enables one-click navigation to source code, significantly improving development efficiency. Here I attempt to read the source code and document key points.&lt;/p>
&lt;/blockquote>
&lt;h2 id="source-code-structure">
&lt;a class="heading-anchor-link" href="#source-code-structure">Source Code Structure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="source-code-structure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The project uses pnpm as the package management tool, with pnpm workspace managing multiple packages.&lt;/p>
&lt;p>The core package is click-to-component-react under packages, while the apps directory contains projects that integrate this component package for testing and validation.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">├── apps
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   ├── cra
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   ├── next
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   └── remix
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── package.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── packages
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   └── click-to-react-component
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── pnpm-lock.yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── pnpm-workspace.yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── turbo.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="source-code-analysis">
&lt;a class="heading-anchor-link" href="#source-code-analysis">Source Code Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="source-code-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Starting from the index entry point for source code interpretation.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">ClickToComponent&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;development&amp;#39;&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">Component&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, NODE_ENV is used to determine if it&amp;rsquo;s a development environment. If it is, the Component is returned; otherwise, a null component is returned. The benefit of this approach is that in production builds, this component won&amp;rsquo;t be included, reducing bundle size.&lt;/p>
&lt;p>When the component loads, the window directly listens for click/contextmenu/keydown/keyup/mousemove/blur events. When clicked, the event can capture the target element.&lt;/p>
&lt;p>In development mode, elements have properties starting with &lt;code>__reactInternalInstance$&lt;/code> or &lt;code>__reactFiber&lt;/code>. These properties point to the fiber object.&lt;/p>
&lt;p>The fiber object has a &lt;code>_debugOwner&lt;/code> property, which points to the parent fiber object. Using the following logic, you can obtain nested fiber objects all the way up to the root fiber object.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">instances&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Set&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">instance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">getReactInstanceForElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">element&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">instance&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">instances&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">instance&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">instance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">instance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_debugOwner&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Additionally, the fiber object has a &lt;code>_debugSource&lt;/code> property, which points to the source code file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">columnNumber&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fileName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lineNumber&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_debugSource&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note: Production builds also have fiber properties on elements, but they lack debug property information&lt;/strong>&lt;/p>
&lt;p>Once you know the source file path, line and column numbers, you just need to combine them with URL Scheme to navigate to the corresponding source file. VSCode has excellent URL Scheme support, refer to &lt;a href="https://code.visualstudio.com/docs/editor/command-line#_opening-vs-code-with-urls" target="_blank" rel="noopener">VSCode URL Scheme&lt;/a>.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This completes the source code analysis of the entire component. The implementation logic isn&amp;rsquo;t complex but tests fundamental knowledge:&lt;/p>
&lt;ol>
&lt;li>React Fiber&lt;/li>
&lt;li>Tree shaking&lt;/li>
&lt;li>Event listening&lt;/li>
&lt;li>URL Scheme&lt;/li>
&lt;/ol>
&lt;p>The component demonstrates how React&amp;rsquo;s internal debugging information can be leveraged to create powerful developer tools that enhance the development experience without impacting production performance.&lt;/p></description></item><item><title>Get GitHub Education Verification for Free Copilot Access</title><link>https://en.1991421.cn/2024/12/27/github/</link><pubDate>Fri, 27 Dec 2024 21:38:18 +0800</pubDate><guid>https://en.1991421.cn/2024/12/27/github/</guid><description>&lt;blockquote>
&lt;p>The better AI code assistance tools are Cursor/GitHub Copilot/JB&amp;rsquo;s AI Assistant. Due to cost considerations, I only subscribe to ChatGPT, but I still need an AI tool for JetBrains IDEs. I decided to use my student identity to get GitHub education verification for free Copilot access.&lt;/p>
&lt;/blockquote>
&lt;h2 id="application">
&lt;a class="heading-anchor-link" href="#application">Application&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="application"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Application URL: &lt;a href="https://education.github.com/pack" target="_blank" rel="noopener">https://education.github.com/pack&lt;/a>&lt;/p>
&lt;p>Just fill in according to the official requirements.&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Your GitHub account email doesn&amp;rsquo;t need to be .edu - as long as identity verification is OK&lt;/li>
&lt;li>During the entire GitHub student verification process, do not use proxies (except for Bing Maps, mentioned below) and keep location permissions enabled for GitHub (required)&lt;/li>
&lt;/ol>
&lt;p>For a complete application guide, I recommend reading &lt;a href="https://github.com/JimmyLing233/Github-Student-Certification-Guide" target="_blank" rel="noopener">this article&lt;/a> contributed by a community member.&lt;/p>
&lt;p>If the review is fast, you&amp;rsquo;ll receive an email with results the same day. Then within a few days, you&amp;rsquo;ll receive GitHub Pro membership and Copilot.&lt;/p>
&lt;h2 id="other-education-verification-benefits">
&lt;a class="heading-anchor-link" href="#other-education-verification-benefits">Other Education Verification Benefits&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-education-verification-benefits"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There are many other benefits from education verification, not just limited to GitHub.&lt;/p>
&lt;p>For example:&lt;/p>
&lt;ol>
&lt;li>JetBrains&lt;/li>
&lt;li>Notion&lt;/li>
&lt;li>Axure&lt;/li>
&lt;/ol></description></item><item><title>Enhance Development Experience with click-to-component</title><link>https://en.1991421.cn/2024/12/26/click-to-component/</link><pubDate>Thu, 26 Dec 2024 10:50:17 +0800</pubDate><guid>https://en.1991421.cn/2024/12/26/click-to-component/</guid><description>&lt;blockquote>
&lt;p>In actual development, finding code often involves copying styles or text from the browser and searching in the IDE. Can I directly click any element on the page and jump to open the corresponding code file in the IDE? click-to-component is such a tool.&lt;/p>
&lt;/blockquote>
&lt;h2 id="effect">
&lt;a class="heading-anchor-link" href="#effect">Effect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="effect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://github.com/ericclemmons/click-to-component/blob/main/.github/next.gif?raw=true"
alt="https://github.com/ericclemmons/click-to-component/blob/main/.github/next.gif?raw=true"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install click-to-component
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Add the following code to your render function. Using environment variables to control the IDE is recommended so individual developers can control which IDE to open. If everyone uses the same IDE, you can hardcode it, like vscode.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ClickToComponent&lt;/span> &lt;span class="na">editor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">REACT_APP_CTC_EDITOR&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ClickToComponent can be placed in any render position; it doesn&amp;rsquo;t have to be in the root component.&lt;/p>
&lt;h2 id="dependencies">
&lt;a class="heading-anchor-link" href="#dependencies">Dependencies&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dependencies"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>click-to-component only requires you to install and configure the component in your project and have the corresponding editor adequately installed, with no other installations needed.&lt;/p>
&lt;p>Bonus: If you have react devtools installed, it will be even better as it will improve the tool&amp;rsquo;s speed in finding components.&lt;/p>
&lt;h2 id="production-build">
&lt;a class="heading-anchor-link" href="#production-build">Production Build&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="production-build"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>ClickToComponent already includes environment variable checks, so for production builds, just ensure NODE_ENV=production, and ClickToComponent won&amp;rsquo;t be included in the build.&lt;/p>
&lt;h2 id="jetbrains-ide-support">
&lt;a class="heading-anchor-link" href="#jetbrains-ide-support">JetBrains IDE Support?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jetbrains-ide-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official repo currently only supports vscode/vscode-insiders/cursor. Regarding JB IDE support, I&amp;rsquo;ve submitted a &lt;a href="https://github.com/ericclemmons/click-to-component/pull/98" target="_blank" rel="noopener">PR&lt;/a> that is waiting to be merged. If you&amp;rsquo;re eager to use it, you can directly reference the commit as a version.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">dependencies:&lt;/span> &lt;span class="err">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;click-to-component&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://github.com/alanhg/click-to-component.git#abc56dfb574efcc49cb31e14743e769c5429c85f&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Using JB is slightly more complicated than vscode.&lt;/p>
&lt;ol>
&lt;li>You need to install JB&amp;rsquo;s plugin &lt;a href="https://plugins.jetbrains.com/plugin/19991-ide-remote-control" target="_blank" rel="noopener">IDE Remote Control&lt;/a>.&lt;/li>
&lt;li>The IDE needs to have the project open.&lt;/li>
&lt;/ol>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>click-to-component&amp;rsquo;s initial purpose was to enhance the development experience by reducing time spent finding code during development and improving development efficiency. I&amp;rsquo;ve been using it for a while now and like it. I recommend that everyone try it.&lt;/li>
&lt;li>If you&amp;rsquo;re curious about how this tool is implemented, check out its source code.&lt;/li>
&lt;/ol>
&lt;h2 id="docs">
&lt;a class="heading-anchor-link" href="#docs">Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/ericclemmons/click-to-component" target="_blank" rel="noopener">click-to-component&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://plugins.jetbrains.com/plugin/19991-ide-remote-control" target="_blank" rel="noopener">IDE Remote Control&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>JetBrains URL Scheme Explained (Simple Guide)</title><link>https://en.1991421.cn/2024/12/25/f590bec/</link><pubDate>Wed, 25 Dec 2024 10:55:11 +0800</pubDate><guid>https://en.1991421.cn/2024/12/25/f590bec/</guid><description>&lt;blockquote>
&lt;p>While using click-to-component recently, I discovered it didn&amp;rsquo;t support JB IDE integration, so I spent some time implementing support. Here, I&amp;rsquo;ll mark down JB&amp;rsquo;s URL Scheme support situation.&lt;/p>
&lt;/blockquote>
&lt;p>JB IDE itself doesn&amp;rsquo;t provide direct URL Scheme support. After research, I found the following 2 solutions.&lt;/p>
&lt;h2 id="jetbrains-toolbox">
&lt;a class="heading-anchor-link" href="#jetbrains-toolbox">JetBrains Toolbox&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jetbrains-toolbox"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After installing the toolbox, it provides support for various JB IDEs.&lt;/p>
&lt;h3 id="supported-ide-list">
&lt;a class="heading-anchor-link" href="#supported-ide-list">Supported IDE List&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supported-ide-list"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>IntelliJ IDEA&lt;/li>
&lt;li>AppCode&lt;/li>
&lt;li>CLion&lt;/li>
&lt;li>PyCharm&lt;/li>
&lt;li>PhpStorm&lt;/li>
&lt;li>RubyMine&lt;/li>
&lt;li>WebStorm&lt;/li>
&lt;li>Rider&lt;/li>
&lt;li>GoLand&lt;/li>
&lt;li>RustRover&lt;/li>
&lt;/ol>
&lt;h3 id="supported-actions">
&lt;a class="heading-anchor-link" href="#supported-actions">Supported Actions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supported-actions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Checkout a repository and open it with the corresponding IDE&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">jetbrains&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">toolTag&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">checkout&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">git&lt;/span>&lt;span class="err">?&lt;/span>&lt;span class="n">checkout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">repo&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">cloneUrl&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">idea&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">required&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">Git4Idea&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Open a repository file and move to the corresponding line and column position.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">jetbrains&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">toolTag&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">navigate&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">reference&lt;/span>&lt;span class="err">?&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">filePath&lt;/span>&lt;span class="p">}:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">lineIndex&lt;/span>&lt;span class="p">}:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="n">columnIndex&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For specific URL Scheme rules, see the repository &lt;a href="https://github.com/alanhe421/jetbrains-url-schemes" target="_blank" rel="noopener">jetbrains-url-schemes&lt;/a>&lt;/p>
&lt;p>The drawback of this solution is that it doesn&amp;rsquo;t provide functionality to open a specific file directly.&lt;/p>
&lt;h2 id="ide-remote-control">
&lt;a class="heading-anchor-link" href="#ide-remote-control">IDE Remote Control&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ide-remote-control"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>JB officially provides IDE Remote Control support, which means the IDE offers a control server that can be operated through HTTP API.&lt;/p>
&lt;h3 id="api-specification">
&lt;a class="heading-anchor-link" href="#api-specification">API Specification&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="api-specification"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">* @apiExample {curl} Absolute path
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* curl http://localhost:63342/api/file//absolute/path/to/file.kt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* @apiExample {curl} Relative path
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* curl http://localhost:63342/api/file/relative/to/module/root/path/to/file.kt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* @apiExample {curl} With line and column
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* curl http://localhost:63342/api/file/relative/to/module/root/path/to/file.kt:100:34
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* @apiExample {curl} Query parameters
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* curl http://localhost:63342/api/file?file=path/to/file.kt&amp;amp;line=100&amp;amp;column=34
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For specific details, see JB&amp;rsquo;s official &lt;a href="https://github.com/JetBrains/intellij-community/blob/a77365debaadcf00b888a977d89512f3f0f3cf9e/platform/built-in-server/src/org/jetbrains/ide/OpenFileHttpService.kt#L52-L59" target="_blank" rel="noopener">commit&lt;/a>&lt;/p>
&lt;p>The drawback of this solution is that IDE needs to install the &lt;code>IDE Remote Control&lt;/code> plugin. And because it&amp;rsquo;s an HTTP service, it might conflict with local ports, requiring manual modification.&lt;/p>
&lt;img src="https://static.1991421.cn/2024/2024-12-25-111637.jpeg" alt="https://static.1991421.cn/2024/2024-12-25-111637.jpeg" style="zoom: 50%;" />
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Both solutions above can achieve file click-and-jump functionality, but both have shortcomings. Looking forward to JB IDE providing comprehensive URL Scheme support directly in the future.&lt;/p>
&lt;h2 id="related-docs">
&lt;a class="heading-anchor-link" href="#related-docs">Related Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://plugins.jetbrains.com/plugin/19991-ide-remote-control" target="_blank" rel="noopener">https://plugins.jetbrains.com/plugin/19991-ide-remote-control&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/JetBrains/intellij-community/tree/master" target="_blank" rel="noopener">https://github.com/JetBrains/intellij-community/tree/master&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>GZIP Compression Not Working?</title><link>https://en.1991421.cn/2024/12/14/gzip/</link><pubDate>Sat, 14 Dec 2024 22:15:28 +0800</pubDate><guid>https://en.1991421.cn/2024/12/14/gzip/</guid><description>&lt;blockquote>
&lt;p>Recently, a site was loading extremely slowly, and analysis revealed that one reason was that GZIP compression wasn&amp;rsquo;t enabled. However, I encountered issues during configuration, so I&amp;rsquo;m documenting this here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="benefits-of-gzip">
&lt;a class="heading-anchor-link" href="#benefits-of-gzip">Benefits of GZIP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benefits-of-gzip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>For text files like JS/HTML, compressed size is about 1/3 of the original, so GZIP should generally be enabled unless there are specific reasons not to.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Note that Nginx doesn&amp;rsquo;t enable GZIP by default, and by default only compresses &lt;code>text/html&lt;/code>. Caddy enables it by default.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="enabling-gzip">
&lt;a class="heading-anchor-link" href="#enabling-gzip">Enabling GZIP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enabling-gzip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using Nginx as an example, the following configuration is needed to enable compression for target file types:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">gzip on;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_types text/plain application/xml text/css application/javascript application/json text/javascript text/html; # Enable compression for specified types
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When I added this configuration to our service, I found it didn&amp;rsquo;t work. It turned out the changes weren&amp;rsquo;t applied to the gateway service.&lt;/p>
&lt;h2 id="service-architecture">
&lt;a class="heading-anchor-link" href="#service-architecture">Service Architecture&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="service-architecture"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let me explain the service architecture, simplified as follows. If GZIP is only configured on Service B, communication between Service A and Service B will use GZIP for faster transmission, but since Service A doesn&amp;rsquo;t have GZIP enabled, Service A will still deliver uncompressed content to the browser client. Therefore, Service A must also have GZIP enabled for it to work.&lt;/p>
&lt;img src="https://static.1991421.cn/2024/2024-12-14-230259.jpeg" alt="https://static.1991421.cn/2024/2024-12-14-230259.jpeg" style="zoom: 50%;" />
&lt;p>When Service A also enables GZIP, actual testing shows that the browser receives compressed content. So does Service B still need to enable it? Personally, I think enabling it is still better since it speeds up communication between A and B, but enabling Service A is more critical.&lt;/p>
&lt;h2 id="factors-determining-gzip-effectiveness">
&lt;a class="heading-anchor-link" href="#factors-determining-gzip-effectiveness">Factors Determining GZIP Effectiveness&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="factors-determining-gzip-effectiveness"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The client request headers include &lt;code>accept-encoding: gzip&lt;/code>, which browsers include by default.&lt;/li>
&lt;li>The server supports and has GZIP enabled.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>While enabling GZIP is simple, it&amp;rsquo;s important to understand how it actually works.&lt;/p></description></item><item><title>useFieldArray in React Hook Form</title><link>https://en.1991421.cn/2024/12/06/react-hook-formusefieldarray/</link><pubDate>Fri, 06 Dec 2024 18:22:02 +0800</pubDate><guid>https://en.1991421.cn/2024/12/06/react-hook-formusefieldarray/</guid><description>&lt;blockquote>
&lt;p>In web page interactions, forms are common elements. For form validation/management, &lt;code>react-hook-form&lt;/code> is a commonly used and excellent helper library. For example, &lt;code>useFieldArray&lt;/code> is used for managing array values, and &lt;code>formContext&lt;/code> is used for complex nested component forms. Understanding these usage patterns helps you use it more effectively.&lt;/p>
&lt;/blockquote>
&lt;h2 id="use-case">
&lt;a class="heading-anchor-link" href="#use-case">Use Case&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-case"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When handling forms, you sometimes need to process array form items, such as when users can dynamically add N rows of form items. useFieldArray is designed for this type of array handling.&lt;/p>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">FormTest&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">control&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">register&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">getValues&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useForm&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">fields&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">append&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">prepend&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">remove&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">swap&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">move&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">insert&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useFieldArray&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">control&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// control props comes from useForm (optional: if you are using FormProvider)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// unique name for your Field Array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fields&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;fields&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">form&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">fields&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">field&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">div&lt;/span>&lt;span class="o">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nx">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">field&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1">// important to include key with field&amp;#39;s id
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">{...&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`test.&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">.value`&lt;/span>&lt;span class="p">)}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">/&amp;gt;&amp;lt;&lt;/span>&lt;span class="err">/div&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">))}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Button&lt;/span> &lt;span class="nx">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">append&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Add&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Button&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getValues&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/form&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As shown above, when clicking the Button to add, fields can automatically insert a new value, and each value can also be monitored and maintained through register/controller.&lt;/p>
&lt;h2 id="important-details">
&lt;a class="heading-anchor-link" href="#important-details">Important Details&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-details"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The append value must be an object - for example, append(1) won&amp;rsquo;t work. This means fields must maintain objects, so &lt;code>test.${index}.value&lt;/code> cannot be set as &lt;code>test.${index}&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-12-06-182851.jpeg"
alt="https://static.1991421.cn/2024/2024-12-06-182851.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For form handling requirements, I recommend trying this library as a solution.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://react-hook-form.com/docs/usefieldarray" target="_blank" rel="noopener">https://react-hook-form.com/docs/usefieldarray&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://react-hook-form.com/advanced-usage" target="_blank" rel="noopener">https://react-hook-form.com/advanced-usage&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Implementing Passwordless Login for Sites in iframes</title><link>https://en.1991421.cn/2024/11/24/iframe/</link><pubDate>Sun, 24 Nov 2024 10:09:11 +0800</pubDate><guid>https://en.1991421.cn/2024/11/24/iframe/</guid><description>&lt;blockquote>
&lt;p>Recently, in a private project, I encountered a requirement where sites embedded in iframes needed to implement passwordless login. Here are some initial thoughts on possible solutions.&lt;/p>
&lt;/blockquote>
&lt;h2 id="ip-whitelist-for-passwordless-login">
&lt;a class="heading-anchor-link" href="#ip-whitelist-for-passwordless-login">IP Whitelist for Passwordless Login?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ip-whitelist-for-passwordless-login"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Any request to the server can obtain the client IP from the request object, making IP-based authentication possible. However, the drawback is that users cannot log in once they change their network environment.&lt;/p>
&lt;h2 id="same-site--cross-domain">
&lt;a class="heading-anchor-link" href="#same-site--cross-domain">Same-Site + Cross-Domain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="same-site--cross-domain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If two sites are same-site but cross-domain, and cookies are set with the parent domain when writing login information, then the cookie information will naturally be carried during requests, enabling passwordless login.&lt;/p>
&lt;h2 id="different-sites">
&lt;a class="heading-anchor-link" href="#different-sites">Different Sites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="different-sites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the two sites are not on the same domain, login needs to be addressed.&lt;/p>
&lt;ol>
&lt;li>When the iframe loads dynamically, it accesses a login link first, and after successful login, redirects to the target page.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This appears to be an incomplete draft exploring various approaches to implementing passwordless authentication for iframe-embedded sites. The key considerations include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Security vs. Convenience&lt;/strong>: IP-based authentication is convenient but lacks flexibility when users change networks&lt;/li>
&lt;li>&lt;strong>Domain Strategy&lt;/strong>: Same-site configurations simplify authentication through shared cookies&lt;/li>
&lt;li>&lt;strong>Cross-Domain Challenges&lt;/strong>: Different domains require more complex authentication mechanisms&lt;/li>
&lt;/ul>
&lt;p>For production implementations, additional considerations would include token-based authentication, OAuth flows, and proper security measures to prevent unauthorized access.&lt;/p></description></item><item><title>Monaco-Editor Implementation of Syntax Highlighting</title><link>https://en.1991421.cn/2024/11/07/ca1d10c/</link><pubDate>Thu, 07 Nov 2024 23:27:55 +0800</pubDate><guid>https://en.1991421.cn/2024/11/07/ca1d10c/</guid><description>&lt;blockquote>
&lt;p>You need to configure the language to implement syntax highlighting in Monaco Editor. Here, we introduce the related operations.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-11-07-231727.jpeg"
alt="https://static.1991421.cn/2024/2024-11-07-231727.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="set-language">
&lt;a class="heading-anchor-link" href="#set-language">Set Language&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="set-language"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If setting the language during initialization, the operation is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">monaco&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">el&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">language&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sol&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If setting the language dynamically:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">editor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getModel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">monaco&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">editor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setModelLanguage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;json&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="built-in-language-support-in-monaco">
&lt;a class="heading-anchor-link" href="#built-in-language-support-in-monaco">Built-in Language Support in Monaco&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="built-in-language-support-in-monaco"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Which languages support syntax highlighting? You can confirm this in several ways.&lt;/p>
&lt;ol>
&lt;li>Retrieve in the program.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">languages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">monaco&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getLanguages&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note: The number of languages retrieved by this method includes custom-registered languages in the application, not limited to built-in languages only.&lt;/strong>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-11-07-230212.jpeg"
alt="https://static.1991421.cn/2024/2024-11-07-230212.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="2">
&lt;li>
&lt;p>Visit &lt;a href="https://microsoft.github.io/monaco-editor/" target="_blank" rel="noopener">https://microsoft.github.io/monaco-editor/&lt;/a> and check the language dropdown options directly.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-11-07-230736.jpeg"
alt="https://static.1991421.cn/2024/2024-11-07-230736.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Directly visit the npm package &lt;code>monaco-editor/esm/vs/basic-languages&lt;/code>, where the built-in languages are registered.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-11-07-231538.jpeg"
alt="https://static.1991421.cn/2024/2024-11-07-231538.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note that each folder name does not necessarily represent the language name; the ID defines the language name.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-11-07-232154.jpeg"
alt="https://static.1991421.cn/2024/2024-11-07-232154.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="pack-language-files-as-needed">
&lt;a class="heading-anchor-link" href="#pack-language-files-as-needed">Pack Language Files as Needed&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pack-language-files-as-needed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Monaco Editor supports many languages by default. However, if you package them all, the file may be too large, so you can pack languages as needed.&lt;/p>
&lt;ol>
&lt;li>To import Monaco Editor:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="err">#&lt;/span> &lt;span class="nx">Correct&lt;/span> &lt;span class="nx">method&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">monaco&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;monaco-editor/esm/vs/editor/editor.api&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">#&lt;/span> &lt;span class="nx">Incorrect&lt;/span> &lt;span class="nx">method&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">monaco&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;monaco-editor&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The webpack configuration is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nx">MonacoWebpackPlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filename&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;monaco_[name].worker.[contenthash].js&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">languages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;json&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;solidity&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sql&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note: If you find that all languages are still packaged after configuring the languages in webpack, it is usually due to an incorrect import method for Monaco.&lt;/strong>&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now, we know how to configure languages in actual projects to achieve syntax highlighting.&lt;/p></description></item><item><title>Which Option to Choose for Git Merge</title><link>https://en.1991421.cn/2024/10/30/git-merge/</link><pubDate>Wed, 30 Oct 2024 18:53:15 +0800</pubDate><guid>https://en.1991421.cn/2024/10/30/git-merge/</guid><description>&lt;blockquote>
&lt;p>When submitting a Merge Request (MR) for a project branch, there are three options: Merge, Rebase, and Squash.&lt;/p>
&lt;p>What are the differences between these three options, and how should you choose? Here’s a summary.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-10-30-190528.jpeg"
alt="https://static.1991421.cn/2024/2024-10-30-190528.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="merge">
&lt;a class="heading-anchor-link" href="#merge">Merge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="merge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The essence of Merge is creating a new commit node, with all commits from the source branch brought over. If there were initially three commits, after the Merge, four commit nodes will exist, with one being the Merge Commit. The commit created by Merge also carries the source branch’s commit information. Therefore, in many Git GUI tools, these details form a commit relationship graph.&lt;/p>
&lt;h2 id="rebase-and-merge">
&lt;a class="heading-anchor-link" href="#rebase-and-merge">Rebase and merge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rebase-and-merge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Rebase modifies the parent node of commits, moving all commits from the source branch to the end of the target branch. If there were three commits initially, after the Rebase, three new commit nodes will appear. Unlike Merge, it does not create a new commit node. Hence, in many Git GUI tools, it appears as a straight line.&lt;/p>
&lt;h2 id="squash-and-merge">
&lt;a class="heading-anchor-link" href="#squash-and-merge">Squash and merge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="squash-and-merge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The essence of Squash is to merge the source branch’s commits into a single commit, generating a new commit message. In many Git GUI tools, this results in a single, latest commit node.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-10-30-220556.jpeg"
alt="https://static.1991421.cn/2024/2024-10-30-220556.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="which-to-choose">
&lt;a class="heading-anchor-link" href="#which-to-choose">Which to Choose?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="which-to-choose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding the differences between the three, how should you decide? Here’s a summary:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Prefer Squash&lt;/strong> because it reduces the number of commits, minimizes redundant commit messages, and makes the commit history clearer. A developer might make multiple commits on their branch. Choosing Merge or Rebase can clutter the main branch with too many commits, whereas Squash reduces the commit count. Of course, developers could also reset and re-commit in their branch, but that is more tedious.&lt;/li>
&lt;li>&lt;strong>Rebase as a secondary option&lt;/strong>, though the downside is that it does not retain merge information. If you don’t care about the merging relationship between branches and only focus on committed content and authors, this can be a good choice.&lt;/li>
&lt;li>&lt;strong>Lastly, Merge&lt;/strong> as it is the simplest and retains the merge relationship between branches. The downside is a more complex relationship graph.&lt;/li>
&lt;/ol></description></item><item><title>How to Use the Custom name in the target attribute of &amp;lt;a&amp;gt; tag (Step-by-Step Guide)</title><link>https://en.1991421.cn/2024/10/24/atarget/</link><pubDate>Thu, 24 Oct 2024 23:05:24 +0800</pubDate><guid>https://en.1991421.cn/2024/10/24/atarget/</guid><description>&lt;blockquote>
&lt;p>I learned about the tag&amp;rsquo;s custom target attribute during the technical weekly meeting, and here is my note.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-10-25-114926.jpeg"
alt="https://static.1991421.cn/2024/2024-10-25-114926.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="mdn-specification">
&lt;a class="heading-anchor-link" href="#mdn-specification">MDN Specification&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mdn-specification"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>HTML target&lt;/p>
&lt;blockquote>
&lt;p>Where to display the linked URL as the name for a browsing context (a tab, window, or &amp;lt;iframe&amp;gt;). The following keywords have special meanings for where to load the URL: _self, _blank, _parent, _top.&lt;/p>
&lt;/blockquote>
&lt;p>As stated in the specification, the above keywords are just special meanings, which means we can define custom names.&lt;/p>
&lt;h2 id="custom-name">
&lt;a class="heading-anchor-link" href="#custom-name">Custom Name&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-name"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, when clicking the following link:&lt;/p>
&lt;ol>
&lt;li>It will open a new TAB window the first time, similar to the effect of _blank.&lt;/li>
&lt;li>Clicking again will not open a new TAB but will switch to the originally opened TAB, and the page will automatically refresh.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://x.com/alanhe421&amp;#34;&lt;/span> &lt;span class="na">target&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;myTarget&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>My Twitter&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This illustrates the meaning of a custom target, which is to specify a custom TAB window to open and switch to the originally opened TAB window.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We can use the custom target based on our example once we understand the custom target. We need it to avoid reopening a new tab. You can use a custom target name.&lt;/p></description></item><item><title>Quickly Fix Bugs in Open Source Community Repositories</title><link>https://en.1991421.cn/2024/10/24/repobug/</link><pubDate>Thu, 24 Oct 2024 11:39:09 +0800</pubDate><guid>https://en.1991421.cn/2024/10/24/repobug/</guid><description>&lt;blockquote>
&lt;p>In projects, we often use many third-party NPM packages, such as Mousetrap. We usually encounter bugs that have not yet been fixed or merged by the official repository. So, what should we do in such cases? Here is a summary of the methods.&lt;/p>
&lt;/blockquote>
&lt;h2 id="fork-the-repository-and-follow-the-pr-process">
&lt;a class="heading-anchor-link" href="#fork-the-repository-and-follow-the-pr-process">Fork the Repository and Follow the PR Process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fork-the-repository-and-follow-the-pr-process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You can fork the repository, modify the source code, submit a PR, and wait for the official team to merge and release a new package. This method is undoubtedly the best; it benefits and gives back to the community.&lt;/p>
&lt;p>However, this approach&amp;rsquo;s downside is also quite noticeable. If the official longed and we urgently need the package, this solution may not be very suitable.&lt;/p>
&lt;h2 id="adjust-npm-dependency-version">
&lt;a class="heading-anchor-link" href="#adjust-npm-dependency-version">Adjust NPM Dependency Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adjust-npm-dependency-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use commit hash or a different repository address. In &lt;code>package.json&lt;/code>, you can use a git commit hash to lock onto a specific version directly, so whether it is merged won’t matter. Alternatively, you can publish a package yourself after forking, but the package name cannot be the same. For example, you can add a &lt;code>@namespace&lt;/code> prefix.&lt;/p>
&lt;h3 id="npm-install-from-git-in-a-specific-version">
&lt;a class="heading-anchor-link" href="#npm-install-from-git-in-a-specific-version">npm install from Git in a specific version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm-install-from-git-in-a-specific-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;dependencies&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;package&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;git://github.com/username/package.git#commit&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="pkg-alias">
&lt;a class="heading-anchor-link" href="#pkg-alias">pkg alias&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pkg-alias"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;dependencies&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;package-a&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npm:@stacker/package-b@1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="patch-package">
&lt;a class="heading-anchor-link" href="#patch-package">patch-package&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="patch-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In addition to the above two solutions, we can use &lt;code>patch-package&lt;/code> to fix bugs quickly. This approach uses &lt;code>git diff&lt;/code> to generate a patch file and then applies it directly using npm scripts hooks to modify files within &lt;code>node_modules&lt;/code>. Without &lt;code>patch-package&lt;/code>, directly modifying source code would not be manageable under version control. &lt;code>patch-package&lt;/code> offers an elegant way to handle conflicts between patches and third-party package source code that isn’t under version control.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Modify the target package source code in &lt;code>node_modules&lt;/code>, such as &lt;code>mousetrap-record.js&lt;/code> under &lt;code>mousetrap&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Generate a patch file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">npx&lt;/span> &lt;span class="nx">patch&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kn">package&lt;/span> &lt;span class="nx">mousetrap&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-10-24-115353.jpeg"
alt="https://static.1991421.cn/2024/2024-10-24-115353.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Configure npm scripts hook&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;postinstall&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;patch-package&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the official repository fixes the bug in the future, we can delete the patch file, remove the package, and simply run &lt;code>npm install&lt;/code>.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above are a few methods for fixing bugs in open-source projects. Use them as needed.&lt;/p></description></item><item><title>Browser Fingerprinting</title><link>https://en.1991421.cn/2024/10/22/browser-fingerprint/</link><pubDate>Tue, 22 Oct 2024 22:38:15 +0800</pubDate><guid>https://en.1991421.cn/2024/10/22/browser-fingerprint/</guid><description>&lt;blockquote>
&lt;p>With logged‑in users, identification is trivial. For anonymous users, we can use a browser fingerprint — a unique identifier derived from available device/browser attributes at a point in time. JS doesn’t expose “a fingerprint” directly; you have to compute it (libraries like FingerprintJS help).&lt;/p>
&lt;/blockquote>
&lt;h2 id="fingerprintjs-v3">
&lt;a class="heading-anchor-link" href="#fingerprintjs-v3">FingerprintJS v3&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fingerprintjs-v3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Latest is v4, but v4 uses BSL (becomes MIT after 4 years). In 2024, v4 isn’t MIT yet, so v3 (MIT) is the safe open‑source choice.&lt;/p>
&lt;h2 id="accuracy">
&lt;a class="heading-anchor-link" href="#accuracy">Accuracy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="accuracy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>OSS accuracy ~40–60%; commercial ~99.5% (with server‑side components).&lt;/li>
&lt;li>Fingerprints often remain stable only for weeks.&lt;/li>
&lt;/ul>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">fpPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">FingerprintJS&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">load&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fpPromise&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fp&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">fp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">result&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">visitorId&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">visitorId&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">visitorId&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/fingerprintjs/fingerprintjs/tree/v3" target="_blank" rel="noopener">https://github.com/fingerprintjs/fingerprintjs/tree/v3&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>ua-parser-js Explained (Simple Guide)</title><link>https://en.1991421.cn/2024/10/18/ua-parser-js/</link><pubDate>Fri, 18 Oct 2024 16:40:22 +0800</pubDate><guid>https://en.1991421.cn/2024/10/18/ua-parser-js/</guid><description>&lt;blockquote>
&lt;p>In real-world development we often parse the UA header to detect device details—whether the user is on Windows or macOS, or if they&amp;rsquo;re on mobile. If you just use &lt;code>userAgent&lt;/code>, you have to parse the string by hand, which is painful. A much easier option is to bring in ua-parser-js, a mature library that surfaces the UA information for you.&lt;/p>
&lt;/blockquote>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s the basic usage:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">UAParser&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getBrowser&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// {&amp;#34;name&amp;#34;:&amp;#34;Chrome&amp;#34;,&amp;#34;version&amp;#34;:&amp;#34;130.0.0.0&amp;#34;,&amp;#34;major&amp;#34;:&amp;#34;130&amp;#34;}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getOS&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// {name: &amp;#39;Mac OS&amp;#39;, version: &amp;#39;10.15.7&amp;#39;}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getDevice&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// {vendor: &amp;#39;Apple&amp;#39;, model: &amp;#39;Macintosh&amp;#39;, type: undefined}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 推荐使用v2版时，开启客户端提示
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">UAParser&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">withClientHints&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The result of UAParser is an object that contains browser, device, OS, engine, CPU, and other details.&lt;/p>
&lt;p>&lt;strong>Notes&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>ua-parser-js v2 is still in beta, but it&amp;rsquo;s recommended because it leverages newer JavaScript features for better accuracy.&lt;/li>
&lt;li>It runs in both Node.js and browser environments.&lt;/li>
&lt;/ol>
&lt;h2 id="typescript-type-safety">
&lt;a class="heading-anchor-link" href="#typescript-type-safety">TypeScript Type Safety&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript-type-safety"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In v1, ua-parser-js didn&amp;rsquo;t include TypeScript definitions by default, so you had to install them manually. v2 bundles the typings, so no manual install is needed.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">npm&lt;/span> &lt;span class="nx">install&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="nx">save&lt;/span> &lt;span class="err">@&lt;/span>&lt;span class="nx">types&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">js&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="bundle-size">
&lt;a class="heading-anchor-link" href="#bundle-size">Bundle Size&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bundle-size"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Both v1 and v2 are about 20 KB.&lt;/p>
&lt;p>Looking at the source, ua-parser-js doesn&amp;rsquo;t set &lt;code>sideEffects&lt;/code>, so even with the ES module build in v2 you won&amp;rsquo;t get tree shaking.&lt;/p>
&lt;h2 id="source-code">
&lt;a class="heading-anchor-link" href="#source-code">Source Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="source-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Under the hood, ua-parser-js parses &lt;code>navigator.userAgent&lt;/code> and &lt;code>navigator.userAgentData&lt;/code>. The logic is straightforward: run regexes and assemble the results into an object.&lt;/p>
&lt;h2 id="limitations">
&lt;a class="heading-anchor-link" href="#limitations">Limitations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="limitations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The code primarily relies on the following APIs:&lt;/p>
&lt;ol>
&lt;li>&lt;code>navigator.userAgent&lt;/code>&lt;/li>
&lt;li>&lt;code>navigator.userAgentData&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>That leads to a few constraints:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>On Apple Silicon Macs, &lt;code>navigator.userAgent&lt;/code> still reports &lt;code>Intel&lt;/code>. That&amp;rsquo;s an OS-level issue in how macOS sends the UA; only Apple can fix it. v2 improves accuracy by tapping into &lt;code>userAgentData&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>navigator.userAgentData&lt;/code> has significant compatibility issues, so even v2 can&amp;rsquo;t guarantee complete coverage across browsers.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-10-18-174433.jpeg"
alt="https://static.1991421.cn/2024/2024-10-18-174433.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If the UA doesn&amp;rsquo;t expose something—like device memory or GPU—you won&amp;rsquo;t get it from ua-parser-js either.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="v2-accuracy-and-user-agent-client-hints">
&lt;a class="heading-anchor-link" href="#v2-accuracy-and-user-agent-client-hints">v2 Accuracy and User-Agent Client Hints&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="v2-accuracy-and-user-agent-client-hints"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In v2, the library calls &lt;code>navigator.userAgentData.getHighEntropyValues&lt;/code> to fetch details like CPU architecture and bitness.&lt;/p>
&lt;p>Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userAgentData&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">getHighEntropyValues&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;architecture&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;bitness&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;formFactor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;fullVersionList&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;platformVersion&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;wow64&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-10-18-180504.jpeg"
alt="https://static.1991421.cn/2024/2024-10-18-180504.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="online-validation">
&lt;a class="heading-anchor-link" href="#online-validation">Online Validation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="online-validation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>The official site lets you enter a UA or choose sample UAs to see the parsed device info:&lt;/p>
&lt;p>&lt;a href="https://uaparser.dev/" target="_blank" rel="noopener">https://uaparser.dev/&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>I tweaked the UI for personal use and host an alternative. It uses the same ua-parser-js version and features:&lt;/p>
&lt;p>&lt;a href="https://uaparser.1991421.cn/" target="_blank" rel="noopener">https://uaparser.1991421.cn/&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That&amp;rsquo;s the quick tour of ua-parser-js. Hope it helps. If you want the most accurate and detailed device information, go with v2.&lt;/p></description></item><item><title>Frontend Visual Graph Editing</title><link>https://en.1991421.cn/2024/10/17/frontend-visual-modeling/</link><pubDate>Thu, 17 Oct 2024 15:39:39 +0800</pubDate><guid>https://en.1991421.cn/2024/10/17/frontend-visual-modeling/</guid><description>&lt;blockquote>
&lt;p>A recent project needed a visual flow editor. Here’s a survey of available libraries and tools.&lt;/p>
&lt;/blockquote>
&lt;h2 id="ibms-open-source-node-redhttpnoderedorg">
&lt;a class="heading-anchor-link" href="#ibms-open-source-node-redhttpnoderedorg">&lt;a href="http://nodered.org/" target="_blank" rel="noopener">IBM&amp;rsquo;s Open-Source Node-RED&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ibms-open-source-node-redhttpnoderedorg"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Apache License&lt;/p>
&lt;h2 id="graph-layout-library-dagre-d3httpsgithubcomcpettittdagre-d3">
&lt;a class="heading-anchor-link" href="#graph-layout-library-dagre-d3httpsgithubcomcpettittdagre-d3">&lt;a href="https://github.com/cpettitt/dagre-d3" target="_blank" rel="noopener">Graph Layout Library dagre-d3&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="graph-layout-library-dagre-d3httpsgithubcomcpettittdagre-d3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MIT License&lt;/p>
&lt;h2 id="graph-layout-library-colajshttpsgithubcomcytoscapecytoscapejs">
&lt;a class="heading-anchor-link" href="#graph-layout-library-colajshttpsgithubcomcytoscapecytoscapejs">&lt;a href="https://github.com/cytoscape/cytoscape.js" target="_blank" rel="noopener">Graph Layout Library cola.js&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="graph-layout-library-colajshttpsgithubcomcytoscapecytoscapejs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MIT License&lt;/p>
&lt;h2 id="open-source-drawing-library-jointhttpsgithubcomclientiojoint">
&lt;a class="heading-anchor-link" href="#open-source-drawing-library-jointhttpsgithubcomclientiojoint">&lt;a href="https://github.com/clientIO/joint" target="_blank" rel="noopener">Open-Source Drawing Library Joint&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-source-drawing-library-jointhttpsgithubcomclientiojoint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MPL-2.0 License + GPLv2 License&lt;/p>
&lt;h2 id="open-source-drawing-library-jsplumbhttpsgithubcomjsplumbcommunity-edition">
&lt;a class="heading-anchor-link" href="#open-source-drawing-library-jsplumbhttpsgithubcomjsplumbcommunity-edition">&lt;a href="https://github.com/jsplumb/community-edition" target="_blank" rel="noopener">Open-Source Drawing Library jsPlumb&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-source-drawing-library-jsplumbhttpsgithubcomjsplumbcommunity-edition"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MIT License&lt;/p>
&lt;h2 id="drawiohttpsgithubcomjgraphdrawio">
&lt;a class="heading-anchor-link" href="#drawiohttpsgithubcomjgraphdrawio">&lt;a href="https://github.com/jgraph/drawio" target="_blank" rel="noopener">drawio&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="drawiohttpsgithubcomjgraphdrawio"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Apache License Version 2.0&lt;/p>
&lt;h2 id="open-source-draw2dhttpsgithubcomjgraphdrawio">
&lt;a class="heading-anchor-link" href="#open-source-draw2dhttpsgithubcomjgraphdrawio">&lt;a href="https://github.com/jgraph/drawio" target="_blank" rel="noopener">Open-Source Draw2D&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-source-draw2dhttpsgithubcomjgraphdrawio"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Apache License Version 2.0&lt;/p>
&lt;h2 id="ggeditorhttpsgithubcomalibabaggeditor">
&lt;a class="heading-anchor-link" href="#ggeditorhttpsgithubcomalibabaggeditor">&lt;a href="https://github.com/alibaba/GGEditor" target="_blank" rel="noopener">GGEditor&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ggeditorhttpsgithubcomalibabaggeditor"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MIT License&lt;/p>
&lt;h2 id="x6httpsgithubcomantvisx6">
&lt;a class="heading-anchor-link" href="#x6httpsgithubcomantvisx6">&lt;a href="https://github.com/antvis/X6" target="_blank" rel="noopener">X6&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="x6httpsgithubcomantvisx6"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MIT License&lt;/p>
&lt;p>&lt;strong>Note: Visual UIs can be built with SVG, Canvas, HTML+CSS, or a mix.&lt;/strong>&lt;/p>
&lt;h2 id="opinion">
&lt;a class="heading-anchor-link" href="#opinion">Opinion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="opinion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Prefer actively maintained projects with an MIT license.&lt;/p></description></item><item><title>Passwordless Login for Linux Machines</title><link>https://en.1991421.cn/2024/09/19/linux/</link><pubDate>Thu, 19 Sep 2024 16:41:56 +0800</pubDate><guid>https://en.1991421.cn/2024/09/19/linux/</guid><description>&lt;blockquote>
&lt;p>For security reasons, passwordless login is generally implemented using public-private key pairs. Here, I&amp;rsquo;ll mark down the configuration method.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>Log into the client machine and create a public-private key pair if you don&amp;rsquo;t already have one.&lt;/p>
&lt;p>The public and private keys are stored in the &lt;code>~/.ssh&lt;/code> directory.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Log into the target server and open the &lt;code>~/.ssh/authorized_keys&lt;/code> file.&lt;/p>
&lt;p>The authorized_keys file records the list of public keys authorized for login.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Copy the public key from the client machine into this file and save it.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Try SSH login to the target machine again, and you&amp;rsquo;ll find no password is required.&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Get the Current Directory of Any Selected File in WebShell</title><link>https://en.1991421.cn/2024/09/19/webshell/</link><pubDate>Thu, 19 Sep 2024 14:14:29 +0800</pubDate><guid>https://en.1991421.cn/2024/09/19/webshell/</guid><description>&lt;blockquote>
&lt;p>While implementing “click to download selected filename” in a WebShell based on xterm.js, I ran into a challenge: how to determine the directory of the selected filename. After some research, here’s the solution.&lt;/p>
&lt;/blockquote>
&lt;h2 id="approach">
&lt;a class="heading-anchor-link" href="#approach">Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>First, have the terminal session report the current directory after each command using shell hooks.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-19-142022.jpeg"
alt="https://static.1991421.cn/2024/2024-09-19-142022.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use &lt;code>registerOscHandler&lt;/code> to listen for ANSI sequences and extract &lt;code>CurrentDir&lt;/code> from the data.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">cwdArr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span> &lt;span class="c1">// index 0 unused
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">registerOscHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1337&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">currenDirArr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">match&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.(&lt;/span>&lt;span class="sr">/(;CurrentDir=)([^;\u0007]+)/&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">currenDirArr&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cwdArr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">active&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">baseY&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">active&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cursorY&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">currenDirArr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>
&lt;p>Listen for selection changes. Combine the selection position with &lt;code>cwdArr&lt;/code> to compute the CWD of the selected filename.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onSelectionChange&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">selection&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getSelection&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">selection&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">selectionPosition&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getSelectionPosition&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getSelection&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">copyToClipboard&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getSelection&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">rowNum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">selectionPosition&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// row
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rowNum&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">cwdArr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="o">!&lt;/span>&lt;span class="nx">cwdArr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">rowNum&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rowNum&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">cwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">cwdArr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">rowNum&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Selected file&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">pathJoin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">cwd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">selection&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This approach lets you infer the working directory that produced any particular section of terminal output and, by extension, the directory of a selected filename.&lt;/p></description></item><item><title>babel-plugin-import Explained (Beginner's Guide)</title><link>https://en.1991421.cn/2024/09/17/babel-plugin-import/</link><pubDate>Tue, 17 Sep 2024 16:01:23 +0800</pubDate><guid>https://en.1991421.cn/2024/09/17/babel-plugin-import/</guid><description>&lt;blockquote>
&lt;p>Due to bundle size issues, I&amp;rsquo;ve revisited babel-plugin-import. I wasn&amp;rsquo;t clear about this plugin before, so I&amp;rsquo;m organizing my understanding here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="plugin-introduction">
&lt;a class="heading-anchor-link" href="#plugin-introduction">Plugin Introduction&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugin-introduction"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>babel-plugin-import&lt;/code> is a Babel plugin that supports modular imports. The functionality of the plugin is to convert full imports to on-demand imports based on configuration.&lt;/p>
&lt;h2 id="problems-solved">
&lt;a class="heading-anchor-link" href="#problems-solved">Problems Solved&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problems-solved"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The import path is still for the full package, making paths shorter.&lt;/li>
&lt;li>On-demand importing, resulting in smaller bundle sizes.&lt;/li>
&lt;/ol>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned officially, classic examples are antd/lodash, though this actually refers more to older versions of antd and lodash.&lt;/p>
&lt;h3 id="antd">
&lt;a class="heading-anchor-link" href="#antd">antd&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="antd"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Button&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;antd&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Button&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;antd/lib/button&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that although we use antd and lodash as examples here, in reality, both packages no longer need babel-plugin-import.&lt;/p>
&lt;ol>
&lt;li>antd now supports tree shaking.&lt;/li>
&lt;li>lodash has released an ES version package lodash-es; switching to that package name enables tree shaking as well.&lt;/li>
&lt;/ol>
&lt;h2 id="does-it-conflict-with-tree-shaking">
&lt;a class="heading-anchor-link" href="#does-it-conflict-with-tree-shaking">Does it Conflict with Tree Shaking?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="does-it-conflict-with-tree-shaking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Not all projects use babel-plugin-import. Why? Because there&amp;rsquo;s tree shaking.&lt;/p>
&lt;p>Are the two in conflict? Not at all, but they have the same ultimate goal: on-demand loading/bundling. When component libraries and the like support tree shaking, the build tool itself will remove unused modules during bundling. Therefore, as long as the component library supports tree shaking, you can use on-demand loading without needing babel-plugin-import.&lt;/p>
&lt;p>Looking at the submission time of the plugin and when webpack started supporting tree shaking, it makes sense why babel-plugin-import was needed before. Because tree shaking wasn&amp;rsquo;t supported at that time.&lt;/p>
&lt;p>Below is the submission time of babel-plugin-import:&lt;/p>
&lt;img src="https://static.1991421.cn/2024/2024-09-18-161956.jpeg" alt="https://static.1991421.cn/2024/2024-09-18-161956.jpeg" style="zoom: 50%;" />
&lt;p>While webpack&amp;rsquo;s support for tree shaking came in webpack v4, which was around &lt;code>2019&lt;/code>.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>OK, that&amp;rsquo;s the analysis of babel-plugin-import.&lt;/p></description></item><item><title>The registry under the npm command does not work</title><link>https://en.1991421.cn/2024/09/17/npmregistrywork/</link><pubDate>Tue, 17 Sep 2024 15:40:08 +0800</pubDate><guid>https://en.1991421.cn/2024/09/17/npmregistrywork/</guid><description>&lt;blockquote>
&lt;p>Sometimes, switching the source of npm packages in a project is necessary due to network or security issues. The general practice is to switch the source in npmrc, but after switching and installing packages, you will find that the install still goes through the old source, primarily when a lock file has already been generated.&lt;/p>
&lt;p>Here, we analyze the issue of the registry not working.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-17-153822.jpeg"
alt="https://static.1991421.cn/2024/2024-09-17-153822.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="version-information">
&lt;a class="heading-anchor-link" href="#version-information">Version Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="version-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For verification, I am using the following versions:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>npm v9.5.1&lt;/p>
&lt;/li>
&lt;li>
&lt;p>node v18.16.1&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="current-lock-file-information">
&lt;a class="heading-anchor-link" href="#current-lock-file-information">Current lock file information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-lock-file-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-17-153847.jpeg"
alt="https://static.1991421.cn/2024/2024-09-17-153847.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Reviewing the lock file, taking the &lt;code>@babel/plugin-proposal-private-property-in-object&lt;/code> package as an example, you will find that the lock file directly records the address under the corresponding package version, here being &lt;a href="https://registry.npmjs.org/" target="_blank" rel="noopener">https://registry.npmjs.org/&lt;/a>.&lt;/p>
&lt;h2 id="npmrc">
&lt;a class="heading-anchor-link" href="#npmrc">npmrc&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npmrc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I tried updating the source via the npmrc file configuration as follows.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">registry=https://registry.npmmirror.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In practical testing, executing &lt;code>npm i&lt;/code> does not download the package from the new source address.&lt;/p>
&lt;h2 id="install---registry">
&lt;a class="heading-anchor-link" href="#install---registry">install &amp;ndash;registry&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install---registry"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since the RC configuration source does not work, try directly overriding the source with npm install &amp;ndash;registry.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm i @babel/plugin-proposal-private-property-in-object --registry&lt;span class="o">=&lt;/span>https://registry.npmmirror.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Actual test executing the install command: &lt;code>--registry&lt;/code> can override installation.&lt;/p>
&lt;p>&lt;strong>What about installing all the packages?&lt;/strong>&lt;/p>
&lt;p>npm i &amp;ndash;registry=https://registry.npmmirror.com&lt;/p>
&lt;p>Tests show no change.&lt;/p>
&lt;p>&lt;strong>What if the lock file is deleted&lt;/strong>&lt;/p>
&lt;p>With the lock file deleted and a re-execution of the npm install, the packages are still cached.&lt;/p>
&lt;p>&lt;strong>Deleting node_modules&lt;/strong>&lt;/p>
&lt;p>After deleting node_modules and re-executing the npm install, the resolved packages are not configured via npmrc.&lt;/p>
&lt;p>&lt;strong>Deleting node_modules, lock file, then executing install &amp;ndash;registry&lt;/strong>&lt;/p>
&lt;p>It is found that all the package download addresses are &lt;a href="https://registry.npmmirror.com" target="_blank" rel="noopener">https://registry.npmmirror.com&lt;/a>. At this time, installing a new package, such as &lt;code>npm install antd&lt;/code>. There is no need to set the registry manually; it will follow the source configured in npmrc.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>From the above tests, it is understood that when installing npm packages, the priority processing of the registry is as follows:&lt;/p>
&lt;ol>
&lt;li>Command parameter &lt;code>--registry&lt;/code>&lt;/li>
&lt;li>Resolved in the lock file/cache&lt;/li>
&lt;li>&lt;code>npmrc&lt;/code> or default registry&lt;/li>
&lt;/ol>
&lt;p>Based on this priority, when we need to change the package source in a project for existing packages, we either need to delete node_modules, package-lock.json and re-execute the package installation, or we need to edit the lock file to replace all registry manual addresses directly. I prefer the latter, as it does not affect the package version.&lt;/p>
&lt;h2 id="links">
&lt;a class="heading-anchor-link" href="#links">Links&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="links"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/npm/rfcs/pull/486" target="_blank" rel="noopener">https://github.com/npm/rfcs/pull/486&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>redux-logger Tree Shaking Not Working</title><link>https://en.1991421.cn/2024/09/14/redux-logger-tree-shakingwork/</link><pubDate>Sat, 14 Sep 2024 15:37:24 +0800</pubDate><guid>https://en.1991421.cn/2024/09/14/redux-logger-tree-shakingwork/</guid><description>&lt;h2 id="issue">
&lt;a class="heading-anchor-link" href="#issue">Issue&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="issue"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Even in Webpack production builds, &lt;code>redux-logger&lt;/code> sticks around. Since the logger is only needed in development, it should disappear. Let’s see why it doesn’t.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-14-152754.jpeg"
alt="https://static.1991421.cn/2024/2024-09-14-152754.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="project-setup">
&lt;a class="heading-anchor-link" href="#project-setup">Project Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;redux-logger&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^3.0.6&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Usage:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createLogger&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;redux-logger&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">logMiddlewares&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;development&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">createLogger&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">collapsed&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">})]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">middleWares&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[...&lt;/span>&lt;span class="nx">logMiddlewares&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createStore&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createRootReducer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">compose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">applyMiddleware&lt;/span>&lt;span class="p">(...&lt;/span>&lt;span class="nx">middleWares&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="looking-into-redux-logger">
&lt;a class="heading-anchor-link" href="#looking-into-redux-logger">Looking into redux-logger&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="looking-into-redux-logger"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;code>&amp;quot;main&amp;quot;: &amp;quot;dist/redux-logger.js&amp;quot;&lt;/code> points at the distribution build. That’s fine—&lt;code>main&lt;/code> can reference either CommonJS or ES modules.&lt;/li>
&lt;li>&lt;code>dist/redux-logger.js&lt;/code> is a UMD bundle that works in CommonJS and AMD environments. Webpack cannot tree-shake UMD output.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-14-153225.jpeg"
alt="https://static.1991421.cn/2024/2024-09-14-153225.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Given the UMD build, it’s expected that tree shaking fails.&lt;/p>
&lt;h2 id="trying-redux-logger-40-beta">
&lt;a class="heading-anchor-link" href="#trying-redux-logger-40-beta">Trying redux-logger 4.0 Beta&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="trying-redux-logger-40-beta"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;redux-logger&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;4.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A beta release on npm ships ES modules, so I tried upgrading. Packaging still retained the logger because tree shaking also requires the &lt;code>sideEffects&lt;/code> flag.&lt;/p>
&lt;p>On the plus side, the import now resolves to an ES module:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-14-153328.jpeg"
alt="https://static.1991421.cn/2024/2024-09-14-153328.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Bundle size at this stage:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-14-153350.jpeg"
alt="https://static.1991421.cn/2024/2024-09-14-153350.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="adding-sideeffects">
&lt;a class="heading-anchor-link" href="#adding-sideeffects">Adding sideEffects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-sideeffects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To confirm the fix, I republished the package with &lt;code>&amp;quot;sideEffects&amp;quot;: false&lt;/code> in &lt;code>package.json&lt;/code>, then analyzed the build again.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-14-153416.jpeg"
alt="https://static.1991421.cn/2024/2024-09-14-153416.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;redux-logger&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;npm:@stacker/redux-logger@4.0.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-14-153451.jpeg"
alt="https://static.1991421.cn/2024/2024-09-14-153451.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-result">
&lt;a class="heading-anchor-link" href="#final-result">Final Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With an ES module build and &lt;code>sideEffects&lt;/code> declared, Webpack finally drops &lt;code>redux-logger&lt;/code> from production bundles.&lt;/p>
&lt;h2 id="reference">
&lt;a class="heading-anchor-link" href="#reference">Reference&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://webpack.js.org/guides/tree-shaking/" target="_blank" rel="noopener">https://webpack.js.org/guides/tree-shaking/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Implementing SQL Autocompletion in Monaco Editor</title><link>https://en.1991421.cn/2024/09/08/monaco-editorsql/</link><pubDate>Sun, 08 Sep 2024 17:34:41 +0800</pubDate><guid>https://en.1991421.cn/2024/09/08/monaco-editorsql/</guid><description>&lt;blockquote>
&lt;p>Recently, I used Monaco editor to implement an SQL editor. To improve the user experience, code auto-completion was necessary. After some research, I found a solution, so I&amp;rsquo;m marking it down here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-08-180231.jpeg"
alt="https://static.1991421.cn/2024/2024-09-08-180231.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="built-in-autocompletion">
&lt;a class="heading-anchor-link" href="#built-in-autocompletion">Built-in Autocompletion?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="built-in-autocompletion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>YES, Monaco supports some built-in languages by default, such as &lt;code>JavaScript, TypeScript, CSS, JSON, and HTML&lt;/code>. You can achieve code autocompletion for languages with built-in support simply by setting the editor language to JavaScript`.&lt;/p>
&lt;p>However, for languages like SQL that do not have built-in support, setting the editor language to SQL only enables syntax highlighting.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">monaco&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">editor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">editorElRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">language&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClosingBrackets&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;always&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClosingOvertype&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;always&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClosingQuotes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;always&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="custom-autocompletion">
&lt;a class="heading-anchor-link" href="#custom-autocompletion">Custom Autocompletion?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-autocompletion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For languages like SQL, if you want autocompletion, you must rely on custom autocompletion.&lt;/p>
&lt;p>Through research, I found that you can register completion logic for the corresponding language using the &lt;code>monaco.languages.registerCompletionItemProvider&lt;/code> method.&lt;/p>
&lt;p>The &lt;code>registerCompletionItemProvider&lt;/code> method allows you to define a list of completion options for the user under different input contexts. This method also supports asynchronous returns, making requesting completion from the backend feasible.&lt;/p>
&lt;h2 id="registercompletionitemprovider-vs-registerinlinecompletionsprovider">
&lt;a class="heading-anchor-link" href="#registercompletionitemprovider-vs-registerinlinecompletionsprovider">registerCompletionItemProvider vs registerInlineCompletionsProvider&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="registercompletionitemprovider-vs-registerinlinecompletionsprovider"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Upon closely examining the API, you will notice two methods related to completion. Generally, we need to implement &lt;code>registerCompletionItemProvider&lt;/code>, but what&amp;rsquo;s the difference between the two?&lt;/p>
&lt;p>&lt;code>registerCompletionItemProvider&lt;/code> presents a list of completion options in a dropdown, as shown in the image at the beginning of the article. In contrast, &lt;code>registerInlineCompletionsProvider&lt;/code> displays completion suggestions directly after the cursor.&lt;/p>
&lt;p>In the context of ZSH, this is similar to the difference between &lt;code>autosuggestion&lt;/code> and &lt;code>autocompletion&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-09-08-224808.jpeg"
alt="https://static.1991421.cn/2024/2024-09-08-224808.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you want to fetch autocompletion suggestions from a backend service, you can implement asynchronous logic within &lt;code>provideCompletionItems&lt;/code>. Here&amp;rsquo;s an example of requesting autocompletion data from an API:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">monaco&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">registerCompletionItemProvider&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">triggerCharacters&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">provideCompletionItems&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">position&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">wordInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">model&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getWordUntilPosition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">position&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">range&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">startLineNumber&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">position&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lineNumber&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">endLineNumber&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">position&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lineNumber&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">startColumn&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">wordInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">startColumn&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">endColumn&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">wordInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">endColumn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">suggestions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/getSqlSuggestions&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;POST&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">body&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">word&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">wordInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">word&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">suggestions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">suggestions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">s&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">kind&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">monaco&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">languages&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CompletionItemKind&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Keyword&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">insertText&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">insertText&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">range&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">range&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Following the above methods, you now understand how to implement SQL code autocompletion in Monaco-editor. Extending this method further, it&amp;rsquo;s possible to implement SQL code autocompletion through AI services since the autocompletion registration method provided by Monaco supports asynchronous returns.&lt;/p></description></item><item><title>How Module Import Style Affects Bundle Size</title><link>https://en.1991421.cn/2024/08/25/frontend-import-size-impact/</link><pubDate>Sun, 25 Aug 2024 22:45:22 +0800</pubDate><guid>https://en.1991421.cn/2024/08/25/frontend-import-size-impact/</guid><description>&lt;blockquote>
&lt;p>We recently discussed the impact of module import styles on bundle size in an internal CR. I think this is quite important, so I’m marking it down here.&lt;/p>
&lt;/blockquote>
&lt;p>Taking a component library as an example, you’ll see lib and es folders in the package. In development, we need to pay attention to how we import modules—different import styles can cause huge differences in bundle size.&lt;/p>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let’s start with an example. The screenshot below shows that importing from the es directory and importing from the package root have the same bundle size, but importing from lib is very different.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-08-25-224824.jpeg"
alt="https://static.1991421.cn/2024/2024-08-25-224824.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Webpack has tree-shaking. When JavaScript uses ES module syntax and is built in production mode, tree-shaking is enabled.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For the component library mentioned above, when we use es imports, it enables tree-shaking. Tools like Webpack will then perform on-demand bundling instead of full bundling.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The package.json of the component library shows both main and module fields declared:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> &amp;#34;main&amp;#34;: &amp;#34;lib/index.js&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;module&amp;#34;: &amp;#34;es/index.js&amp;#34;,
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Frontend bundle size has always been an important issue, with solutions like CDN, on-demand loading, on-demand bundling, and GZIP. The import style issue discussed above is just one aspect of this problem.&lt;/p></description></item><item><title>Setting Environment Variables during SSH2 Connection</title><link>https://en.1991421.cn/2024/08/20/ssh2/</link><pubDate>Tue, 20 Aug 2024 23:11:21 +0800</pubDate><guid>https://en.1991421.cn/2024/08/20/ssh2/</guid><description>&lt;blockquote>
&lt;p>Can environment variables be set when connecting to a server using the Node.js SSH2 client? After investigation, it was found that it is possible. Just marking it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="setting-environment-variables">
&lt;a class="heading-anchor-link" href="#setting-environment-variables">Setting Environment Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setting-environment-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The code is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">shell&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">env&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">X_LOGIN_IP&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;128.1.1.123&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">X_ENVIRONMENT_VARIABLE&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;desiredvalue&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="nx">term&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;xterm-256color&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// term: &amp;#39;dumb&amp;#39;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">cols&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">connectOpts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cols&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rows&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">connectOpts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">rows&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ssh shell error&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="limitations">
&lt;a class="heading-anchor-link" href="#limitations">Limitations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="limitations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This environment variable setting is restricted by whether the &lt;code>/etc/ssh/sshd_config&lt;/code> allows setting specific rule variables. Different systems have different defaults for the allowed environment variable settings.&lt;/p>
&lt;p>If you need to allow specific variables, the setting directive is &lt;code>AcceptEnv&lt;/code>. The example below supports setting environment variables with the X_ prefix.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-08-20-160215.jpeg"
alt="https://static.1991421.cn/2024/2024-08-20-160215.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="default-supported-environment-variables">
&lt;a class="heading-anchor-link" href="#default-supported-environment-variables">Default Supported Environment Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="default-supported-environment-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned earlier, different systems have different default settings.&lt;/p>
&lt;ul>
&lt;li>Ubuntu defaults to &lt;code>AcceptEnv LANG LC_*&lt;/code>&lt;/li>
&lt;li>Debian defaults to &lt;code>AcceptEnv LANG LC_*&lt;/code>&lt;/li>
&lt;li>CentOS9 does not have AcceptEnv set by default, meaning no environment variables can be set.&lt;/li>
&lt;/ul>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That&amp;rsquo;s all you need to know about setting environment variables on the client side.&lt;/p></description></item><item><title>Monitor Network Disconnection in WEB</title><link>https://en.1991421.cn/2024/08/12/web-detect-network-disconnect/</link><pubDate>Mon, 12 Aug 2024 23:57:13 +0800</pubDate><guid>https://en.1991421.cn/2024/08/12/web-detect-network-disconnect/</guid><description>&lt;blockquote>
&lt;p>Recently, I received a front-end issue about garbled text caused by the user&amp;rsquo;s network disconnection and recovery. After investigation, I found that the issue was caused by the WEB&amp;rsquo;s lack of monitoring and handling of network changes. Here are some methods to mark.&lt;/p>
&lt;/blockquote>
&lt;h2 id="monitor-network-changes">
&lt;a class="heading-anchor-link" href="#monitor-network-changes">Monitor Network Changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="monitor-network-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Monitor network changes through the online/offline events. For example, the offline event is triggered when Wi-Fi is turned off, and the online event is triggered when it is turned on.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;online&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Network is online&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;offline&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Network is offline&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="check-network-status">
&lt;a class="heading-anchor-link" href="#check-network-status">Check Network Status&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="check-network-status"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The following method can be used to check the network status in real time.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onLine&lt;/span> &lt;span class="c1">// true/false
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="simulate-network-disconnection-in-chrome">
&lt;a class="heading-anchor-link" href="#simulate-network-disconnection-in-chrome">Simulate Network Disconnection in Chrome&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="simulate-network-disconnection-in-chrome"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we understand how to monitor network changes and check the network status, how can we simulate a network disconnection? Fortunately, in Chrome, you can directly set the page to offline mode without changing the entire device&amp;rsquo;s network status.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-08-13-000514.jpeg"
alt="https://static.1991421.cn/2024/2024-08-13-000514.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="will-websocket-connection-disconnect-when-offline">
&lt;a class="heading-anchor-link" href="#will-websocket-connection-disconnect-when-offline">Will WebSocket Connection Disconnect when Offline?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="will-websocket-connection-disconnect-when-offline"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The WebSocket connection will not be immediately disconnected when the browser is offline. Moreover, when the network is restored, the data in the WebSocket will continue to be sent to the backend.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding network changes and how to check network status can help better handle issues caused by network disconnection.&lt;/p></description></item><item><title>ShellCheck Explained (Simple Guide)</title><link>https://en.1991421.cn/2024/08/07/shellcheck/</link><pubDate>Wed, 07 Aug 2024 19:03:20 +0800</pubDate><guid>https://en.1991421.cn/2024/08/07/shellcheck/</guid><description>&lt;blockquote>
&lt;p>I recently had to evaluate ShellCheck for work and found it quite useful. ShellCheck detects—and often fixes—syntax issues in shell scripts, so I&amp;rsquo;m jotting down my notes here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>ShellCheck is cross-platform, supports multiple operating systems, and many apps integrate it, so you have plenty of ways to use it.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Use it directly in the browser: &lt;a href="https://www.shellcheck.net" target="_blank" rel="noopener">ShellCheck Web&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Visual Studio Code extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck" target="_blank" rel="noopener">ShellCheck&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>JetBrains IDEs include the &lt;a href="https://www.jetbrains.com/help/idea/shell-scripts.html" target="_blank" rel="noopener">Shell Script&lt;/a> plugin; no extra install needed, though in my testing it doesn&amp;rsquo;t expose the full ShellCheck feature set.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Command-line installation&lt;/p>
&lt;ol>
&lt;li>
&lt;p>macOS: &lt;code>brew install shellcheck&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>EPEL-based distros&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo yum -y install epel-release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo yum install ShellCheck
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>See the &lt;a href="https://github.com/koalaman/shellcheck?tab=readme-ov-file#installing" target="_blank" rel="noopener">official instructions&lt;/a> for the full list of installation methods.&lt;/p>
&lt;h2 id="shell-support">
&lt;a class="heading-anchor-link" href="#shell-support">Shell Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="shell-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>ShellCheck&amp;rsquo;s static analysis doesn&amp;rsquo;t cover every shell:&lt;/p>
&lt;ol>
&lt;li>Supported: &lt;code>bash&lt;/code>, &lt;code>sh&lt;/code>, &lt;code>ksh&lt;/code>, and &lt;code>zsh&lt;/code>.&lt;/li>
&lt;li>Not supported: &lt;code>fish&lt;/code>, &lt;code>tcsh&lt;/code>, and &lt;code>csh&lt;/code>.&lt;/li>
&lt;li>&lt;code>zsh&lt;/code> and &lt;code>ksh&lt;/code> support only works where they are compatible with &lt;code>bash&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h2 id="features">
&lt;a class="heading-anchor-link" href="#features">Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Whether you run it manually or automatically, ShellCheck&amp;rsquo;s job is to highlight syntax mistakes in your shell scripts.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-08-07-204617.jpeg"
alt="https://static.1991421.cn/2024/2024-08-07-204617.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;a href="https://github.com/koalaman/shellcheck/blob/master/README.md#user-content-gallery-of-bad-code" target="_blank" rel="noopener">Examples&lt;/a>&lt;/p>
&lt;h2 id="web-support">
&lt;a class="heading-anchor-link" href="#web-support">Web Support?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I also looked into running ShellCheck entirely in the browser.&lt;/p>
&lt;ol>
&lt;li>The official web demo sends your script to the server and runs ShellCheck there.&lt;/li>
&lt;li>Full client-side execution isn&amp;rsquo;t officially supported right now, but converting the Haskell codebase to WASM might work—no firm conclusion yet.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you write shell scripts regularly, install the plugin in your editor—it can save you from many avoidable syntax errors.&lt;/p>
&lt;h2 id="related-docs">
&lt;a class="heading-anchor-link" href="#related-docs">Related Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://github.com/koalaman/shellcheck/issues/2548#issuecomment-2270210173" target="_blank" rel="noopener">https://github.com/koalaman/shellcheck/issues/2548#issuecomment-2270210173&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://gitlab.haskell.org/ghc/ghc-wasm-meta" target="_blank" rel="noopener">https://gitlab.haskell.org/ghc/ghc-wasm-meta&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Wave Terminal Explained (Beginner's Guide)</title><link>https://en.1991421.cn/2024/07/21/wave-terminal/</link><pubDate>Sun, 21 Jul 2024 23:34:47 +0800</pubDate><guid>https://en.1991421.cn/2024/07/21/wave-terminal/</guid><description>&lt;blockquote>
&lt;p>Due to work needs, I have used many terminals, such as iTerm2, Warp, Tabby, Hyper, etc.&lt;/p>
&lt;p>Recently, I researched Wave Terminal and found some highlights, so I&amp;rsquo;ll introduce it here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://framerusercontent.com/images/v4mqnli1KXaGOKANhD2O2OBRzk.png"
alt="https://framerusercontent.com/images/v4mqnli1KXaGOKANhD2O2OBRzk.png"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="platform">
&lt;a class="heading-anchor-link" href="#platform">Platform&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="platform"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Wave is a local terminal, not web-based, currently supporting.&lt;/p>
&lt;ol>
&lt;li>Mac (M series also supported)&lt;/li>
&lt;li>Linux&lt;/li>
&lt;li>Windows WSL (Windows is also supported!)&lt;/li>
&lt;/ol>
&lt;p>For the Mac version, it&amp;rsquo;s about 224MB, which is acceptable.&lt;/p>
&lt;h2 id="free-and-open-source">
&lt;a class="heading-anchor-link" href="#free-and-open-source">Free and Open Source&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="free-and-open-source"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Wave is an open-source and free terminal. It has similarities with Warp, such as block mode display, and is therefore also known as an open-source free Warp alternative.&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s how to use it. First, Wave doesn&amp;rsquo;t require a login, which is a 👍 point.&lt;/p>
&lt;h3 id="connection">
&lt;a class="heading-anchor-link" href="#connection">Connection&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="connection"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To log in to a remote machine, first create a connection.&lt;/p>
&lt;img src="https://static.1991421.cn/2024/2024-07-21-230850.jpeg" alt="https://static.1991421.cn/2024/2024-07-21-230850.jpeg" style="zoom: 50%;" />
&lt;p>After creating, you can try to connect. If prompted to install remote scripts, click install, and once the connection is successful, you&amp;rsquo;re good to go.&lt;/p>
&lt;p>Regarding connection configuration, the path to delete connection configurations is too cumbersome. Currently, you need to: select connection - delete - confirm deletion.&lt;/p>
&lt;h3 id="workspace">
&lt;a class="heading-anchor-link" href="#workspace">Workspace&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="workspace"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In Wave, click on a workspace, then click Create New Tab, and choose the terminal connection to log in to the target machine. You can also customize the theme color of the session tab.&lt;/p>
&lt;p>Workspace here is a form of session grouping. In practice, it can be divided into production/development/testing, etc., to avoid operational errors.&lt;/p>
&lt;h3 id="command-history">
&lt;a class="heading-anchor-link" href="#command-history">Command History&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="command-history"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Wave records all commands executed in the terminal, including the command content, terminal connection, execution time, and more.&lt;/p>
&lt;img src="https://static.1991421.cn/2024/2024-07-21-225300.jpeg" alt="https://static.1991421.cn/2024/2024-07-21-225300.jpeg" style="zoom: 25%;" />
&lt;p>In addition to searching commands on the history page, you can also summon history reminders in actual terminal sessions with hotkeys for quick access.&lt;/p>
&lt;p>The history is recorded in the local App database at &lt;code>$HOME/.waveterm/waveterm.db&lt;/code>.&lt;/p>
&lt;p>Commands in the history can be summoned and executed in any terminal session. You can choose commands from the history of the current machine or any command from across machines.&lt;/p>
&lt;h3 id="file-edit">
&lt;a class="heading-anchor-link" href="#file-edit">File Edit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="file-edit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>Wave is weak in file-related operations. The only support is to input &lt;code>codeedit filename&lt;/code> in the terminal to invoke file editing. You can set it to split-screen or block display mode.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>It does not support the &lt;code>lrzsz&lt;/code> command for file upload/download, nor does it provide visual file management.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://mintlify.s3-us-west-1.amazonaws.com/commandline/images/codeedit.gif"
alt="https://mintlify.s3-us-west-1.amazonaws.com/commandline/images/codeedit.gif"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="others">
&lt;a class="heading-anchor-link" href="#others">Others&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="others"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The above are the core functions of Wave. Besides, Wave also provides some common features, such as AI and command completion.&lt;/p>
&lt;p>Wave AI supports local AI models and cloud models (only supports OpenAI).&lt;/p>
&lt;p>Usage:&lt;/p>
&lt;ol>
&lt;li>Right-side AI chat entrance&lt;/li>
&lt;li>&lt;code>/chat&lt;/code> command in terminal sessions&lt;/li>
&lt;li>AI icon in command blocks&lt;/li>
&lt;/ol>
&lt;p>Regarding command completion, I find it quite useless at the moment, so I won&amp;rsquo;t introduce it.&lt;/p>
&lt;p>OK, that&amp;rsquo;s all for Wave&amp;rsquo;s basic functions. Now for my personal view.&lt;/p>
&lt;h2 id="advantages-and-disadvantages">
&lt;a class="heading-anchor-link" href="#advantages-and-disadvantages">Advantages and disadvantages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages-and-disadvantages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="disadvantages">
&lt;a class="heading-anchor-link" href="#disadvantages">disadvantages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="disadvantages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>As stated in the Wave open-source project&amp;rsquo;s issues, there are many problems in actual use, such as ineffective connection configuration deletion, ineffective configuration editing, and failure to install remote shell on remote machines, etc. You will encounter many issues in actual use. However, since the Wave open-source project started on Jun 5, 2022, we need to give it more time to develop. Its version number is not yet v1.0. Since it&amp;rsquo;s an open-source project, you can contribute as well, be it through issues or PRs.&lt;/p>
&lt;h3 id="advantages">
&lt;a class="heading-anchor-link" href="#advantages">Advantages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>The workspace organization is very innovative.&lt;/li>
&lt;li>The terminal supports input commands to operate features, such as &lt;code>/chat&lt;/code> for AI Q&amp;amp;A, and&lt;code>/codeedit&lt;/code> for file editing.&lt;/li>
&lt;/ol>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Wave is still very immature. For users who frequently use terminals, I do not recommend using them because the issues are so many that they can hinder usage.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For infrequent users who can tolerate the issues, you can use it. After all, it is an open-source free terminal with Warp&amp;rsquo;s block display feature.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="related-documents">
&lt;a class="heading-anchor-link" href="#related-documents">Related Documents&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documents"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/wavetermdev/waveterm" target="_blank" rel="noopener">Wave Open Source Project&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.waveterm.dev/introduction" target="_blank" rel="noopener">Wave Official Documentation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Accessing Google Cloud VMs from Your Local Terminal</title><link>https://en.1991421.cn/2024/07/12/google/</link><pubDate>Fri, 12 Jul 2024 23:42:23 +0800</pubDate><guid>https://en.1991421.cn/2024/07/12/google/</guid><description>&lt;blockquote>
&lt;p>I recently spent some time exploring Google&amp;rsquo;s Identity-Aware Proxy (IAP) and experimented with different ways to manage Google Cloud VMs from my local machine. Here’s a summary of the setup process for future reference.&lt;/p>
&lt;/blockquote>
&lt;h2 id="1-getting-started">
&lt;a class="heading-anchor-link" href="#1-getting-started">1. Getting Started&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-getting-started"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To use GCP, you&amp;rsquo;ll need:&lt;/p>
&lt;ol>
&lt;li>A &lt;strong>Gmail&lt;/strong> account.&lt;/li>
&lt;li>A &lt;strong>Payment Method&lt;/strong>: You cannot create instances without a valid credit card on file.
&lt;ul>
&lt;li>&lt;em>Note: Be careful with free credits. Even if your console shows a balance, these credits have an expiration date. Once they expire, you will be charged real money for any active resources.&lt;/em>&lt;/li>
&lt;li>I used a standard Visa card, and the verification was straightforward.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="2-creating-an-instance">
&lt;a class="heading-anchor-link" href="#2-creating-an-instance">2. Creating an Instance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-creating-an-instance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>GCP bills Compute Engine (VM) instances per second. However, since the rates are in USD, costs can escalate quickly if you aren&amp;rsquo;t careful. If you are just testing, remember to &lt;strong>Delete&lt;/strong> (not just stop) your instance as soon as you are done.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-07-12-234514.jpeg"
alt="GCP VM Creation"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Configuration Tip&lt;/strong>: After creation, you can edit the instance to disable its public IP for better security. You can also enable detailed logging, but keep in mind that log storage itself is a billable service.&lt;/p>
&lt;h2 id="3-connecting-to-your-vm">
&lt;a class="heading-anchor-link" href="#3-connecting-to-your-vm">3. Connecting to Your VM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-connecting-to-your-vm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>GCP provides several browser-based connection methods, such as &lt;strong>Cloud Shell&lt;/strong> and the integrated &lt;strong>Web SSH client&lt;/strong>. However, for a better development experience, you&amp;rsquo;ll want to connect from your local terminal.&lt;/p>
&lt;h3 id="method-a-with-a-public-ip">
&lt;a class="heading-anchor-link" href="#method-a-with-a-public-ip">Method A: With a Public IP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="method-a-with-a-public-ip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If your instance has a public IP address and you&amp;rsquo;ve allowed SSH traffic (port 22) in the firewall rules, you can use standard SSH.&lt;/p>
&lt;h3 id="method-b-without-a-public-ip-via-iap">
&lt;a class="heading-anchor-link" href="#method-b-without-a-public-ip-via-iap">Method B: Without a Public IP (via IAP)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="method-b-without-a-public-ip-via-iap"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;strong>IAP (Identity-Aware Proxy)&lt;/strong> allows you to connect to VMs that do not have external IP addresses by tunneling through Google&amp;rsquo;s infrastructure.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Configure IAP Permissions&lt;/strong>: In the GCP Console, go to the IAP settings and grant the &amp;ldquo;IAP-secured Tunnel User&amp;rdquo; role to your Google account for the specific resource.&lt;/li>
&lt;li>&lt;strong>Firewall Rules&lt;/strong>: Ensure you have a firewall rule that allows ingress traffic from the IP range &lt;code>35.235.240.0/20&lt;/code> on port 22. This is the range Google uses for IAP tunneling.&lt;/li>
&lt;li>&lt;strong>Install the Google Cloud SDK (gcloud CLI)&lt;/strong>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Example for Linux/macOS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tar -xf google-cloud-cli-linux-x86_64.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./google-cloud-sdk/install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>Initialize the CLI&lt;/strong>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gcloud init
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>Follow the prompts to log in via your browser and select your project and default region.&lt;/li>
&lt;li>&lt;strong>Connect via Tunnel&lt;/strong>:
Once configured, use the following command to SSH into your private VM:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">gcloud compute ssh &amp;lt;INSTANCE_NAME&amp;gt; &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --zone &lt;span class="s2">&amp;#34;&amp;lt;ZONE&amp;gt;&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tunnel-through-iap &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --project &lt;span class="s2">&amp;#34;&amp;lt;PROJECT_ID&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Connecting via IAP is a much more secure way to manage your cloud infrastructure, as it eliminates the need to expose port 22 to the entire internet. It takes a few minutes to set up, but the security benefits are well worth it. Happy tunneling!&lt;/p></description></item><item><title>Customizing Source Map Generation in Webpack</title><link>https://en.1991421.cn/2024/06/30/webpacksourcemap/</link><pubDate>Sun, 30 Jun 2024 16:43:02 +0800</pubDate><guid>https://en.1991421.cn/2024/06/30/webpacksourcemap/</guid><description>&lt;blockquote>
&lt;p>For production web releases, we enable Source Maps to trace errors back to source code via our RUM platform. Not every bundle needs a Source Map though. How can we customize which files get one?&lt;/p>
&lt;/blockquote>
&lt;h2 id="enable-source-maps">
&lt;a class="heading-anchor-link" href="#enable-source-maps">Enable Source Maps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-source-maps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In Webpack, a simple configuration looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">devtool&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;source-map&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The downside is that every JS file will get a Source Map. Suppose you want to exclude certain files, e.g., lazily loaded third‑party modules like Monaco Editor. Webpack supports customizing Source Map generation.&lt;/p>
&lt;h2 id="customize-source-maps">
&lt;a class="heading-anchor-link" href="#customize-source-maps">Customize Source Maps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="customize-source-maps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Make sure to disable &lt;code>devtool: 'source-map'&lt;/code> (set it to false), otherwise the plugin config below won’t take effect.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">devtool&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nx">webpack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SourceMapDevToolPlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filename&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;[file].map[query]&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">exclude&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="sr">/monaco_/&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With this configuration, files whose names contain &lt;code>monaco_&lt;/code> will be excluded from Source Map generation.&lt;/p>
&lt;p>Note: when bundling JS with content hashes in filenames, enabling Source Maps adds a &lt;code>//# sourceMappingURL=xxx.4ea8e146beed0e49c7c5.js.map&lt;/code> comment at the end of the JS file, which changes the file content—and thus its content hash.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Done.&lt;/p></description></item><item><title>Feasibility of Specifying a Startup File When Opening an SSH2 Session</title><link>https://en.1991421.cn/2024/06/29/ssh2/</link><pubDate>Sat, 29 Jun 2024 23:51:19 +0800</pubDate><guid>https://en.1991421.cn/2024/06/29/ssh2/</guid><description>&lt;blockquote>
&lt;p>While reading the inshellisense code, I noticed it controls loading a startup file from a specific directory when spawning a local shell. That made me wonder: can an SSH2 web terminal session do something similar? Here’s a quick feasibility discussion.&lt;/p>
&lt;/blockquote>
&lt;h2 id="inshellisenses-approach">
&lt;a class="heading-anchor-link" href="#inshellisenses-approach">inshellisense’s approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="inshellisenses-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Taking bash as an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">shellArgs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;--init-file&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">shellFolderPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;shellIntegration.bash&amp;#34;&lt;/span>&lt;span class="p">)];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="err">#&lt;/span>&lt;span class="nx">pty&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">pty&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">shellTarget&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shellArgs&lt;/span> &lt;span class="o">??&lt;/span> &lt;span class="p">[],&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;xterm-256color&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cols&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rows&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cwd&lt;/span>: &lt;span class="kt">process.cwd&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">env&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">convertToPtyEnv&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">shell&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">underTest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">login&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">env&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This works by passing shell-specific arguments as the second parameter to &lt;code>spawn&lt;/code>; for bash, there is an &lt;code>--init-file&lt;/code> option.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-29-235653.jpeg"
alt="https://static.1991421.cn/2024/2024-06-29-235653.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="ssh2">
&lt;a class="heading-anchor-link" href="#ssh2">SSH2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ssh2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://github.com/mscdex/ssh2?tab=readme-ov-file#client-methods" target="_blank" rel="noopener">https://github.com/mscdex/ssh2?tab=readme-ov-file#client-methods&lt;/a>&lt;/p>
&lt;p>There is no analogous parameter in the SSH2 &lt;code>connect&lt;/code> method, so you can’t pass a startup file that way.&lt;/p>
&lt;p>Two possible workarounds:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>In an interactive session, run &lt;code>source&lt;/code> to load your integration script. Downside: it will produce visible echo/output artifacts.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">stream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Modify the shell’s startup file (e.g., &lt;code>.bashrc&lt;/code>) to load your script by default. This avoids echo issues on subsequent connections.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>​&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Done.&lt;/p></description></item><item><title>OpenCloudOS SSH exec errors</title><link>https://en.1991421.cn/2024/06/12/2e548447/</link><pubDate>Wed, 12 Jun 2024 23:04:12 +0800</pubDate><guid>https://en.1991421.cn/2024/06/12/2e548447/</guid><description>&lt;blockquote>
&lt;p>WebShell SSH login to OpenCloudOS 9 disconnects immediately. After some investigation I found the root cause - here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="webshell-ssh-login-flow">
&lt;a class="heading-anchor-link" href="#webshell-ssh-login-flow">WebShell SSH login flow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webshell-ssh-login-flow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>WebShell uses nodejs-ssh2 to implement terminal login:&lt;/p>
&lt;ol>
&lt;li>SSH connect opens an interactive shell for login.&lt;/li>
&lt;li>After successful login, the underlying SSH connection is reused to open exec channels to run commands. Here, four commands were executed concurrently.&lt;/li>
&lt;/ol>
&lt;h2 id="error-logs">
&lt;a class="heading-anchor-link" href="#error-logs">Error logs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-logs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With debug logs enabled, you&amp;rsquo;ll see something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Inbound: CHANNEL_DATA (r:0, 57)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Inbound: CHANNEL_OPEN_CONFIRMATION (r:1, s:1)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Outbound: Sending CHANNEL_REQUEST (r:1, exec: env)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Inbound: CHANNEL_OPEN_CONFIRMATION (r:2, s:2)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Outbound: Sending CHANNEL_REQUEST (r:2, exec: curl https://1991421.cn)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Inbound: CHANNEL_OPEN_CONFIRMATION (r:3, s:3)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Outbound: Sending CHANNEL_REQUEST (r:3, exec: pwd)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Inbound: CHANNEL_OPEN_CONFIRMATION (r:4, s:4)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Outbound: Sending CHANNEL_REQUEST (r:4, exec: id)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Socket ended
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Socket closed
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/Users/alanhe/GitHub/express-demo/node_modules/ssh2/lib/client.js:1836
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> cb(had_err !== true ? had_err : new Error(&amp;#39;Unable to exec&amp;#39;)); ^
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, the connection drops directly, leading to the final error &amp;ldquo;Unable to exec&amp;rdquo;.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>If I disable command execution or keep only one command, it works.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The machine had 1 vCPU and 1 GB RAM. Upgrading to something like 8 GB removed the error.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Reinstalling OpenCloudOS 8 with the same config worked fine.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>OpenCloudOS 9 uses OpenSSH v9.3, while OpenCloudOS 8 uses v8.x. Testing other images like Fedora 40 with OpenSSH v9.6 worked fine.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Therefore, I suspect v9 has some limit that causes the connection to drop when too many exec channels are opened. This does not appear to be a general OpenSSH 9.x compatibility issue.&lt;/p>
&lt;h3 id="check-server-side-openssh-version">
&lt;a class="heading-anchor-link" href="#check-server-side-openssh-version">Check server-side OpenSSH version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="check-server-side-openssh-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ssh -V
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-13-181408.jpeg"
alt="https://static.1991421.cn/2024/2024-06-13-181408.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="fixes">
&lt;a class="heading-anchor-link" href="#fixes">Fixes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fixes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Ideally OpenCloudOS 9 should fix this SSH issue - other systems don&amp;rsquo;t exhibit it.&lt;/li>
&lt;li>Until the specific limit is identified, disable or avoid concurrent execs to work around it.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>While I haven&amp;rsquo;t pinpointed exactly where the OS enforces this limit, the cause is clear enough. Running commands in parallel isn&amp;rsquo;t inherently wrong and shouldn&amp;rsquo;t be a problem, but don&amp;rsquo;t open too many channels - settings like MaxSessions will still cap things.&lt;/p></description></item><item><title>tar: Ignoring unknown extended header keyword</title><link>https://en.1991421.cn/2024/06/11/tar-ignoring-unknown-extended-header-keyword-libarchive-xattr-com-apple-metadata-kmditemtextcontentlanguage/</link><pubDate>Tue, 11 Jun 2024 19:34:32 +0800</pubDate><guid>https://en.1991421.cn/2024/06/11/tar-ignoring-unknown-extended-header-keyword-libarchive-xattr-com-apple-metadata-kmditemtextcontentlanguage/</guid><description>&lt;blockquote>
&lt;p>After creating a tar archive on macOS and extracting on CentOS/Linux, you may see: &lt;code>tar: Ignoring unknown extended header keyword LIBARCHIVE.xattr.com.apple.metadata:kMDItemTextContentLanguage&lt;/code>. This is due to differences in macOS tar. Notes below.&lt;/p>
&lt;/blockquote>
&lt;h2 id="problem">
&lt;a class="heading-anchor-link" href="#problem">Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>On macOS, create the archive:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">tar -czvf ../xxxx.tar.gz --exclude&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;.DS_Store&amp;#39;&lt;/span> xxxx/*
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>On CentOS, extract:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">tar -C xx/lib -xz
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You’ll see output like:&lt;/p>
&lt;blockquote>
&lt;p>tar: Ignoring unknown extended header keyword
LIBARCHIVE.xattr.com.apple.metadata:kMDItemTextContentLanguage&lt;/p>
&lt;/blockquote>
&lt;h2 id="why">
&lt;a class="heading-anchor-link" href="#why">Why&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>macOS uses BSD tar and writes xattr headers. CentOS commonly uses GNU tar.&lt;/li>
&lt;li>The message is informational; extraction still works.&lt;/li>
&lt;/ol>
&lt;h2 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Install GNU tar on macOS: &lt;code>brew install gnu-tar&lt;/code>, then create with &lt;code>gtar -czvf ../xxxx.tar.gz --exclude='.DS_Store' xxxx/*&lt;/code>.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/51655657/tar-ignoring-unknown-extended-header-keyword-libarchive-xattr-security-selinux" target="_blank" rel="noopener">https://stackoverflow.com/questions/51655657/tar-ignoring-unknown-extended-header-keyword-libarchive-xattr-security-selinux&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Detect CPU Architecture in Shell Scripts</title><link>https://en.1991421.cn/2024/06/11/shellcpu/</link><pubDate>Tue, 11 Jun 2024 18:27:47 +0800</pubDate><guid>https://en.1991421.cn/2024/06/11/shellcpu/</guid><description>&lt;blockquote>
&lt;p>While writing an installer for Code Server, I needed to identify the machine’s CPU architecture to fetch the right tarball.&lt;/p>
&lt;/blockquote>
&lt;h2 id="code-server-packages">
&lt;a class="heading-anchor-link" href="#code-server-packages">Code Server Packages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code-server-packages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Different architectures require different builds:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-11-183158.jpeg"
alt="https://static.1991421.cn/2024/2024-06-11-183158.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="script">
&lt;a class="heading-anchor-link" href="#script">Script&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="script"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>uname&lt;/code> reports the architecture:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">arch&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">uname_m&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>uname -m&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nv">$uname_m&lt;/span> in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> aarch64&lt;span class="o">)&lt;/span> &lt;span class="nb">echo&lt;/span> arm64 &lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> x86_64&lt;span class="o">)&lt;/span> &lt;span class="nb">echo&lt;/span> amd64 &lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *&lt;span class="o">)&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$uname_m&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">esac&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">ARCH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">ARCH&lt;/span>&lt;span class="k">:-$(&lt;/span>arch&lt;span class="k">)&lt;/span>&lt;span class="si">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you know the architecture, downloading the matching package is trivial.&lt;/p>
&lt;h2 id="cloud-server-architectures">
&lt;a class="heading-anchor-link" href="#cloud-server-architectures">Cloud Server Architectures&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cloud-server-architectures"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Cloud providers (e.g., Tencent Cloud) display CPU architecture when you provision instances, so you can choose what you need up front:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-11-184222.jpeg"
alt="https://static.1991421.cn/2024/2024-06-11-184222.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-notes">
&lt;a class="heading-anchor-link" href="#final-notes">Final Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s it—simple and effective.&lt;/p></description></item><item><title>Enable Autosuggestions in Your Shell</title><link>https://en.1991421.cn/2024/06/10/shellautosuggestion/</link><pubDate>Mon, 10 Jun 2024 23:30:15 +0800</pubDate><guid>https://en.1991421.cn/2024/06/10/shellautosuggestion/</guid><description>&lt;blockquote>
&lt;p>Some terminals show ghosted command suggestions as you type. Press → to accept them instantly—that feature is called &lt;strong>autosuggestion&lt;/strong>.&lt;/p>
&lt;/blockquote>
&lt;h2 id="autosuggestion-vs-autocompletion">
&lt;a class="heading-anchor-link" href="#autosuggestion-vs-autocompletion">Autosuggestion vs. Autocompletion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="autosuggestion-vs-autocompletion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>They’re different concepts.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Autosuggestion&lt;/strong> leverages history or heuristics to display a full command after the cursor. Press one key to accept it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-10-131006.jpeg"
alt="https://static.1991421.cn/2024/2024-06-10-131006.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Autocompletion&lt;/strong> uses command metadata to offer options when you press a trigger key (usually Tab). It doesn’t fill the entire command—just the current argument—and often produces multiple candidates.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-10-223521.jpeg"
alt="https://static.1991421.cn/2024/2024-06-10-223521.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Both can coexist without conflict.&lt;/p>
&lt;h2 id="enabling-autosuggestions">
&lt;a class="heading-anchor-link" href="#enabling-autosuggestions">Enabling Autosuggestions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enabling-autosuggestions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Each shell handles it differently.&lt;/p>
&lt;h3 id="bash">
&lt;a class="heading-anchor-link" href="#bash">Bash&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>No official community solution yet. Some terminals, like Warp, implement their own.&lt;/p>
&lt;h3 id="zsh">
&lt;a class="heading-anchor-link" href="#zsh">Zsh&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="zsh"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Install via Oh My Zsh:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install Oh My Zsh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sh -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Enable the zsh-autosuggestions plugin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">plugins&lt;/span>&lt;span class="o">=(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> zsh-autosuggestions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="fish">
&lt;a class="heading-anchor-link" href="#fish">Fish&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fish"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Fish ships with autosuggestions enabled—no extra configuration required.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Turn on autosuggestions. Many commands repeat; accepting suggestions saves time and effort.&lt;/p></description></item><item><title>FileSaver Source Code Analysis</title><link>https://en.1991421.cn/2024/06/08/filesaver/</link><pubDate>Sat, 08 Jun 2024 15:03:32 +0800</pubDate><guid>https://en.1991421.cn/2024/06/08/filesaver/</guid><description>&lt;blockquote>
&lt;p>Web file download can use the mature library &lt;a href="https://github.com/eligrey/FileSaver.js" target="_blank" rel="noopener">FileSaver.js&lt;/a>, which is only 2KB after compression, so introducing it won&amp;rsquo;t be much of a size burden on the site. At the same time, we also need to understand the implementation of this package. Here we&amp;rsquo;ll organize the implementation logic.&lt;/p>
&lt;/blockquote>
&lt;h2 id="saveas-api">
&lt;a class="heading-anchor-link" href="#saveas-api">saveAs API&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="saveas-api"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First look at the parameters of the API method. You can see that you can directly pass in file data or URL.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">FileSaver&lt;/span> &lt;span class="nx">saveAs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Blob&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">File&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">Url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">optional&lt;/span> &lt;span class="nx">DOMString&lt;/span> &lt;span class="nx">filename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">optional&lt;/span> &lt;span class="nb">Object&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">autoBom&lt;/span> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="processing-flow">
&lt;a class="heading-anchor-link" href="#processing-flow">Processing Flow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="processing-flow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Manipulate DOM to create an anchor element. Since the anchor element has no content set, it won&amp;rsquo;t appear on the current page.&lt;/li>
&lt;li>Set the download attribute of the anchor element, with the value being the specified filename.&lt;/li>
&lt;li>Set the rel value of the anchor element to noopener, which benefits performance and security.&lt;/li>
&lt;li>Determine if the save storage is a string
&lt;ol>
&lt;li>If it&amp;rsquo;s a string, it will be treated as a URL. Then, based on whether it&amp;rsquo;s cross-origin, decide whether to use the anchor tag for direct download and save. If cross-origin, the download attribute won&amp;rsquo;t work, so XHR must be used to download data and then save as blob. If not cross-origin, the anchor tag can directly hang the URL and download to ensure download and save.&lt;/li>
&lt;li>If it&amp;rsquo;s not a URL, the blob data is directly used as the href of the anchor element, and a.click() is triggered.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;h2 id="download-file-instead-of-opening-file">
&lt;a class="heading-anchor-link" href="#download-file-instead-of-opening-file">Download File Instead of Opening File&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download-file-instead-of-opening-file"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When we directly download files via URL, we&amp;rsquo;ll find that the browser will open the file by default. If we want to change this behavior, there are several ways:&lt;/p>
&lt;ol>
&lt;li>For same-origin file downloads, just set the download attribute in the anchor tag method. The browser will treat it as setting Content-Disposition:attachment, so it will download as an attachment.&lt;/li>
&lt;li>For non-same-origin file downloads, the download attribute won&amp;rsquo;t work, so you need to asynchronously download the file to get blob data, then construct an anchor tag to save, or have the server return with Content-Disposition:attachment set.&lt;/li>
&lt;/ol>
&lt;h2 id="prevent-memory-leaks">
&lt;a class="heading-anchor-link" href="#prevent-memory-leaks">Prevent Memory Leaks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prevent-memory-leaks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>You can notice that after the anchor element&amp;rsquo;s href is bound to objectURL, it will use setTimeout and then execute click download, followed by delayed execution of &lt;code>URL.revokeObjectURL(a.href)&lt;/code>. This is mainly done to worry about memory leaks when files are too large, requiring manual memory cleanup.&lt;/li>
&lt;li>Since the anchor element is not appended to document.body, there&amp;rsquo;s no manual deletion of the anchor element, so revoke is used here. If the anchor element were bound to document.body, this could actually be solved by deleting the element.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Done.&lt;/p></description></item><item><title>Security Component Scanning and Handling in Node Projects</title><link>https://en.1991421.cn/2024/06/02/node/</link><pubDate>Sun, 02 Jun 2024 12:37:04 +0800</pubDate><guid>https://en.1991421.cn/2024/06/02/node/</guid><description>&lt;blockquote>
&lt;p>The company has jobs that scan R&amp;amp;D projects daily. For Node projects, they promptly alert about component vulnerabilities in packages. When necessary, we need to handle them promptly, as security is no small matter. Here&amp;rsquo;s a summary of the approach.&lt;/p>
&lt;/blockquote>
&lt;p>Component vulnerabilities generally refer to npm package versions that have records in open-source vulnerability databases, meaning the community has already informed that this version has vulnerabilities, so it&amp;rsquo;s recommended to upgrade to certain versions.&lt;/p>
&lt;p>When receiving such notifications, what needs to be done is to upgrade as soon as possible. Of course, actual operations can be divided into several situations:&lt;/p>
&lt;ol>
&lt;li>Direct dependencies
If the package is a direct dependency, then simply upgrade directly. Just note that if it&amp;rsquo;s a major version update, consider whether there are usage changes. If so, you need to adjust the calling approach. Of course, more importantly, you need to verify. If the impact is significant, reduce the release speed - safety first.&lt;/li>
&lt;li>Indirect dependencies
If the package is an indirect dependency, it&amp;rsquo;s a bit more troublesome. Priority is to find the direct dependency and upgrade it. If the direct dependency package doesn&amp;rsquo;t have a new version, you can use resolution to directly control the package version. Of course, here you also need to determine whether the version upgrade is a major version update and what the risk is for the fix release.&lt;/li>
&lt;/ol>
&lt;h2 id="npm-ls-pkg">
&lt;a class="heading-anchor-link" href="#npm-ls-pkg">npm ls pkg&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm-ls-pkg"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s a small tip: using npm ls can quickly determine a package&amp;rsquo;s dependency tree structure in the project. Based on the version information provided in security reports, you can quickly determine whether it&amp;rsquo;s a direct or indirect dependency. Even if it&amp;rsquo;s indirect, you can clearly know which package introduced that version.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-06-02-124526.jpeg"
alt="https://static.1991421.cn/2024/2024-06-02-124526.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="npm-check">
&lt;a class="heading-anchor-link" href="#npm-check">npm-check&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm-check"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Whether using npm-check or the built-in interactive update checking in yarn/pnpm, they are excellent ways to query version upgrade information. I recommend using them.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>done. Security is no small matter. It&amp;rsquo;s recommended to carefully review each exposed security vulnerability, not just update a version number.&lt;/p></description></item><item><title>Ping Requests in the Browser Network Panel</title><link>https://en.1991421.cn/2024/05/31/105fa5b/</link><pubDate>Fri, 31 May 2024 00:14:27 +0800</pubDate><guid>https://en.1991421.cn/2024/05/31/105fa5b/</guid><description>&lt;blockquote>
&lt;p>Ping requests pop up in Chrome DevTools all the time. I wanted to know what triggers them and what they actually do, so here’s the breakdown.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-05-31-190626.jpeg"
alt="https://static.1991421.cn/2024/2024-05-31-190626.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="navigatorsendbeacon">
&lt;a class="heading-anchor-link" href="#navigatorsendbeacon">&lt;code>navigator.sendBeacon&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="navigatorsendbeacon"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A little digging shows that those ping entries come from &lt;code>navigator.sendBeacon&lt;/code>. The call can carry data, and under the hood it still issues an HTTP POST, so the server should treat it exactly like any other POST.&lt;/p>
&lt;p>The browser sends the request asynchronously via &lt;code>navigator&lt;/code>, not from the page’s single-threaded JavaScript context. Because of that, closing or navigating away from the page doesn’t interrupt it.&lt;/p>
&lt;p>&lt;strong>Example&lt;/strong>&lt;/p>
&lt;p>When a page unloads, you can use &lt;code>sendBeacon&lt;/code> to tell the server the user has closed the tab. With XHR or &lt;code>fetch&lt;/code>, the page might disappear before the request ever leaves the browser, but &lt;code>sendBeacon&lt;/code> finishes reliably.&lt;/p>
&lt;p>Of course there are edge cases—if the browser crashes or the computer powers off, nothing gets sent.&lt;/p>
&lt;h2 id="cross-browser-display-differences">
&lt;a class="heading-anchor-link" href="#cross-browser-display-differences">Cross-Browser Display Differences&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cross-browser-display-differences"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>sendBeacon&lt;/code> appears differently depending on the browser.&lt;/p>
&lt;ol>
&lt;li>Chrome labels it as &lt;code>ping&lt;/code>.&lt;/li>
&lt;li>Safari labels it as &lt;code>beacon&lt;/code>.&lt;/li>
&lt;li>Firefox also labels it as &lt;code>beacon&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>One annoyance in Chrome is that you can’t filter directly by the ping type in the Network tab—you have to choose “Other” or filter by URL.&lt;/p>
&lt;h2 id="how-to-fire-a-ping-request">
&lt;a class="heading-anchor-link" href="#how-to-fire-a-ping-request">How to Fire a Ping Request&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-fire-a-ping-request"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here’s a basic example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sendBeacon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/beacon&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Blob&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;hello&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})],&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/json&amp;#39;&lt;/span>&lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="use-cases">
&lt;a class="heading-anchor-link" href="#use-cases">Use Cases&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-cases"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The behavior makes it ideal for analytics beacons, especially when you need to send data as the page is closing.&lt;/p>
&lt;p>Analytics libraries in the wild often rely on &lt;code>sendBeacon&lt;/code> for this exact purpose.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s everything I’ve gathered so far.&lt;/p>
&lt;h2 id="further-reading">
&lt;a class="heading-anchor-link" href="#further-reading">Further Reading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="further-reading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/75666416/type-ping-in-network-tab" target="_blank" rel="noopener">https://stackoverflow.com/questions/75666416/type-ping-in-network-tab&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>login shell</title><link>https://en.1991421.cn/2024/05/15/login-shell/</link><pubDate>Wed, 15 May 2024 21:26:32 +0800</pubDate><guid>https://en.1991421.cn/2024/05/15/login-shell/</guid><description>&lt;blockquote>
&lt;p>A WebShell user recently reported that logging into a Debian terminal wasn&amp;rsquo;t loading &lt;code>~/.profile&lt;/code>. After digging around, I found they were dropped into an interactive non-login shell, so the login-only configs never ran.&lt;/p>
&lt;/blockquote>
&lt;h2 id="login-shell">
&lt;a class="heading-anchor-link" href="#login-shell">Login Shell&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="login-shell"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A login shell loads &lt;code>~/.bash_profile&lt;/code>, &lt;code>.profile&lt;/code>, and &lt;code>/etc/profile&lt;/code>. A non-login shell skips those but does source &lt;code>~/.bashrc&lt;/code>.&lt;/p>
&lt;h2 id="profile-load-order">
&lt;a class="heading-anchor-link" href="#profile-load-order">Profile Load Order&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="profile-load-order"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;code>/etc/profile&lt;/code>: the shell reads the global configuration first.&lt;/li>
&lt;li>&lt;code>~/.bash_profile&lt;/code>, &lt;code>~/.bash_login&lt;/code>, and &lt;code>~/.profile&lt;/code> (in that order): it stops at the first file that exists and is readable. If &lt;code>~/.bash_profile&lt;/code> is present, the shell doesn&amp;rsquo;t process the others; otherwise it falls back to the next.&lt;/li>
&lt;/ol></description></item><item><title>File Contents Cleared When Writing via Stream</title><link>https://en.1991421.cn/2024/04/27/stream-write-file-cleared/</link><pubDate>Sat, 27 Apr 2024 16:40:58 +0800</pubDate><guid>https://en.1991421.cn/2024/04/27/stream-write-file-cleared/</guid><description>&lt;blockquote>
&lt;p>The ssh2-sftp-client provides a put method, which can accept a readable stream to continuously write data to a remote file. Recently encountered an issue where if the program terminates when the write stream just started, the file contents are cleared. Here we analyze the cause.&lt;/p>
&lt;/blockquote>
&lt;p>In the put method, localSrc can be a readable stream. remotePath is the file path on the remote server.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">async&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">localSrc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">remotePath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">localSrc&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;string&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">localCheck&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">haveLocalAccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">localSrc&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">localCheck&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fmtError&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">`Bad path: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">localSrc&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">localCheck&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">details&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;put&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">localCheck&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">code&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">localSrc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">remotePath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">custom&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fmtError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Re-thrown: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;put&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">code&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Based on the remote file path, sftp creates a writable stream, writeStreamOptions sets some configuration for the writable stream. The readable stream continuously reads and writes into the SFTP writable stream.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">lPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opts&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">addListeners&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">listeners&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">wtr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rdr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">addListeners&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">listeners&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">addTempListeners&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;_put&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">opts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readStreamOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">opts&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readStreamOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">autoClose&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeStreamOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">opts&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeStreamOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">autoClose&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pipeOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">opts&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipeOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">end&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">haveConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;_put&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createWriteStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeStreamOptions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">lPath&lt;/span> &lt;span class="k">instanceof&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">debugMsg&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;put source is a buffer&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">end&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">lPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rdr&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">lPath&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;string&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createReadStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">lPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readStreamOptions&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="nx">lPath&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rdr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wtr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipeOptions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">addListeners&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeTempListeners&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">listeners&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;_put&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The SFTP writable stream is based on Node.js writable stream with inheritance modifications. If flag is not set, it defaults to &lt;code>w&lt;/code>.&lt;/p>
&lt;p>Check NodeJS official documentation: &lt;code>'w'&lt;/code>: Open file for writing. The file is created (if it does not exist) or truncated (if it exists).&lt;/p>
&lt;p>Now we can know the content is cleared because of this &amp;lsquo;w&amp;rsquo;. But how does SFTP get affected by this flag? Let&amp;rsquo;s continue.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">WriteStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">WritableStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">flags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">flags&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;w&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="mo">0o666&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">autoClose&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">autoClose&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">autoClose&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pos&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bytesWritten&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isClosed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handle&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="kc">null&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handle&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">sftp&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_opening&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">checkPosition&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pos&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">encoding&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setDefaultEncoding&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">encoding&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Node v6.x only
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;finish&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_writableState&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">finalCalled&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">autoClose&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">destroy&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">Buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handle&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first step of the write stream is to open the stream, and the flags parameter is passed during opening. In the sftp open method, stringToFlags(flags_) converts the stream&amp;rsquo;s flag into the SFTP protocol&amp;rsquo;s file opening flag value.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">WriteStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_opening&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_opening&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">er&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">handle&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_opening&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">er&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;error&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">er&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">autoClose&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">destroy&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">handle&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tryAgain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Try chmod() for sftp servers that may not support fchmod() for
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// whatever reason
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chmod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err_&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">tryAgain&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// SFTPv3 requires absolute offsets, no matter the open flag used
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tryStat&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">st&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Try stat() for sftp servers that may not support fstat() for
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// whatever reason
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">st_&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err_&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">destroy&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;error&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tryStat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">st_&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pos&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">st&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">size&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;open&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">handle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ready&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fstat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">handle&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">tryStat&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;open&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">handle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">emit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ready&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fchmod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">handle&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">tryAgain&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">flags_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">attrs&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cb&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Client-only method called in server mode&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;function&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">attrs&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">flags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">flags_&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;number&amp;#39;&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">flags_&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">stringToFlags&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">flags_&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">flags&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Unknown flags string: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">flags_&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">attrsFlags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">attrsLen&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;string&amp;#39;&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;number&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">mode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">attrs&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;object&amp;#39;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">attrs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">attrsToBytes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">attrs&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">attrsFlags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">attrs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">flags&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">attrsLen&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">attrs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint32 id
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> string filename
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> uint32 pflags
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> ATTRS attrs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">pathLen&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">byteLength&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">p&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">9&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">buf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">allocUnsafe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">pathLen&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">attrsLen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeUInt32BE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">buf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">REQUEST&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">OPEN&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">reqid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_writeReqid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_writeReqid&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nx">MAX_REQID&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeUInt32BE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reqid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeUInt32BE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">pathLen&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">utf8Write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">pathLen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeUInt32BE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nx">pathLen&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeUInt32BE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">attrsFlags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">attrsLen&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">p&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">attrsLen&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">ATTRS_BUF&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ATTRS_BUF&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">bufferCopy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ATTRS_BUF&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">attrsLen&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">p&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nx">attrsLen&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_requests&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">reqid&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">cb&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">isBuffered&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">sendOrBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">buf&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_debug&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_debug&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">`SFTP: Outbound: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">isBuffered&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;Buffered&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Sending&amp;#39;&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> OPEN`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">stringFlagMap&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;r&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;r+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;w&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TRUNC&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;wx&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TRUNC&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;xw&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TRUNC&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;w+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TRUNC&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;wx+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TRUNC&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;xw+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TRUNC&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">APPEND&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ax&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">APPEND&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;xa&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">APPEND&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;a+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">APPEND&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ax+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">APPEND&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;xa+&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">APPEND&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CREAT&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">READ&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WRITE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="nx">OPEN_MODE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">EXCL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we understand why it becomes empty: when uploading a file, if the original file exists, it actually truncates first.&lt;/p>
&lt;p>So how to avoid this problem?&lt;/p>
&lt;p>The solution is to first upload to a temporary directory when using sftp upload, then mv rename to the original file for safety.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://nodejs.org/api/fs.html#file-system-flags" target="_blank" rel="noopener">https://nodejs.org/api/fs.html#file-system-flags&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/theophilusx/ssh2-sftp-client?tab=readme-ov-file#org90c60ca" target="_blank" rel="noopener">https://github.com/theophilusx/ssh2-sftp-client?tab=readme-ov-file#org90c60ca&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>parseInt</title><link>https://en.1991421.cn/2024/04/22/parseint/</link><pubDate>Mon, 22 Apr 2024 23:37:56 +0800</pubDate><guid>https://en.1991421.cn/2024/04/22/parseint/</guid><description>&lt;blockquote>
&lt;p>Today I saw a parseInt question on our company&amp;rsquo;s internal platform, and the result was surprising. The root cause was unclear understanding of parseInt execution, so I&amp;rsquo;m documenting it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="problem">
&lt;a class="heading-anchor-link" href="#problem">Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0xf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 15
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0xf&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 15
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mh">0xf&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 21
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">15&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 21
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="explanation">
&lt;a class="heading-anchor-link" href="#explanation">Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The first parameter of parseInt starts from the first non-whitespace character. For non-string values, it performs ToString conversion, so parseInt(15,16) is essentially parseInt(&amp;lsquo;15&amp;rsquo;,16).&lt;/li>
&lt;li>parseInt(&amp;lsquo;0xf&amp;rsquo;,16) - JavaScript recognizes the string as a hexadecimal number, and the radix is also hexadecimal, so it directly parses to 15. parseInt(&amp;lsquo;0xf&amp;rsquo;) doesn&amp;rsquo;t pass a radix, but based on the 0x prefix in the string, it&amp;rsquo;s still recognized as hexadecimal, so it&amp;rsquo;s still 15.&lt;/li>
&lt;/ol>
&lt;h2 id="interview-question">
&lt;a class="heading-anchor-link" href="#interview-question">Interview Question&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="interview-question"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding the processing logic, let&amp;rsquo;s analyze this code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;2&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;3&amp;#39;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The callback logic for the above program is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// NaN
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// NaN
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>radix 0 is equivalent to base 10. radix 1 results in NaN. Base 2 cannot have 3, so it results in NaN.&lt;/p>
&lt;p>Personally, I don&amp;rsquo;t think memorizing these processing logic has much value. The real valuable points are:&lt;/p>
&lt;ol>
&lt;li>Pay attention to automatic type conversion - there are many such issues in JavaScript.&lt;/li>
&lt;li>Be careful with callback writing like map(parseInt) to avoid accidentally passing the index and causing unexpected results.&lt;/li>
&lt;/ol></description></item><item><title>Serverless via Tencent Cloud Functions Explained (Simple Guide)</title><link>https://en.1991421.cn/2024/04/20/serverless/</link><pubDate>Sat, 20 Apr 2024 22:48:20 +0800</pubDate><guid>https://en.1991421.cn/2024/04/20/serverless/</guid><description>&lt;blockquote>
&lt;p>I’ve used Tencent Cloud Functions a few times now. They’re basically Tencent’s serverless offering. Documenting the scenarios helped me internalize what “serverless” really means.&lt;/p>
&lt;/blockquote>
&lt;h2 id="use-case-1-lightweight-middleware-for-a-mini-program">
&lt;a class="heading-anchor-link" href="#use-case-1-lightweight-middleware-for-a-mini-program">Use case 1: lightweight middleware for a mini program&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-case-1-lightweight-middleware-for-a-mini-program"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A WeChat mini program needed a small middle tier—mostly proxying calls to a larger backend and performing simple DB lookups. Spinning up my own server felt wasteful, so I deployed a cloud function instead. Zero ops overhead.&lt;/p>
&lt;h2 id="use-case-2-sandboxed-ai-scripting">
&lt;a class="heading-anchor-link" href="#use-case-2-sandboxed-ai-scripting">Use case 2: sandboxed AI scripting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-case-2-sandboxed-ai-scripting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Users can write scripts to customize AI chat behavior. Inputs/outputs are standardized, but the actual logic runs on our platform. I first considered Web Workers, but domestic AI providers often block cross-origin requests. Running scripts inside the main backend introduces security risk. Cloud Functions offered a sandboxed environment with server-side network access—perfect.&lt;/p>
&lt;h2 id="use-case-3-quick-proxy-service">
&lt;a class="heading-anchor-link" href="#use-case-3-quick-proxy-service">Use case 3: quick proxy service&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-case-3-quick-proxy-service"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I needed a small proxy. Buying a server means dealing with hardware specs, domains, and maintenance. With Cloud Functions I just upload the proxy code. No domain needed, scaling is automatic, and logging is configurable with minimal effort.&lt;/p>
&lt;h2 id="sowhat-is-serverless">
&lt;a class="heading-anchor-link" href="#sowhat-is-serverless">So…what is serverless?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sowhat-is-serverless"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Re-reading the definition after real-world use makes it click:&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>Serverless is a cloud-native development model that lets developers build and run applications without managing servers.&lt;/em> Servers still exist, but the cloud provider handles maintenance, updates, scaling, and other undifferentiated heavy lifting. Developers package code, deploy it, and let the provider’s elasticity do the rest.&lt;/p>
&lt;/blockquote>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Buying a VPS puts you at the machine-granularity level—you manage everything. Serverless moves the abstraction down to individual functions. With Cloud Functions you focus entirely on application logic and ignore infrastructure.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://aws.amazon.com/cn/campaigns/serverless/" target="_blank" rel="noopener">https://aws.amazon.com/cn/campaigns/serverless/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use SSE in AI Chat (Step-by-Step Guide)</title><link>https://en.1991421.cn/2024/04/13/aisse/</link><pubDate>Sat, 13 Apr 2024 14:54:45 +0800</pubDate><guid>https://en.1991421.cn/2024/04/13/aisse/</guid><description>&lt;blockquote>
&lt;p>ChatGPT has been popular for a year now。It has also driven the use of SSE.&lt;/p>
&lt;p>Here, I summarize my understanding of it.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-04-13-153102.jpeg"
alt="https://static.1991421.cn/2024/2024-04-13-153102.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="advantages-of-sse">
&lt;a class="heading-anchor-link" href="#advantages-of-sse">Advantages of SSE&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages-of-sse"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Uses the standard HTTP protocol, and not WebSocket. Relative to WebSocket, it has a smaller resource overhead.&lt;/li>
&lt;li>SSE transmits text data with simple overhead.&lt;/li>
&lt;/ol>
&lt;h2 id="how-to-use">
&lt;a class="heading-anchor-link" href="#how-to-use">How to Use&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-use"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource" target="_blank" rel="noopener">EventSource&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">evtSource&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">EventSource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;sse.php&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">evtSource&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onmessage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`message: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/fetch" target="_blank" rel="noopener">Fetch&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/openai/stream&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;post&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">headers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/json;charset=utf-8&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">body&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">reader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getReader&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">buffer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">readChunk&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">read&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">done&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Stream finished&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">chunkString&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">TextDecoder&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buffer&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nx">chunkString&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">position&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nx">position&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\n\n&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">completeMessage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">slice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">position&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buffer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">slice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">position&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">completeMessage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">line&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">line&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">startsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data:&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">jsonText&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">line&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">slice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">jsonText&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;[DONE]&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;done&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">dataObject&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">jsonText&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dataObject&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;JSON parse error:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">jsonText&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readChunk&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readChunk&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="fetch-vs-eventsource">
&lt;a class="heading-anchor-link" href="#fetch-vs-eventsource">Fetch vs EventSource&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fetch-vs-eventsource"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Both have roughly the same compatibility.&lt;/li>
&lt;li>A disadvantage of EventSource is that it cannot send a request body, so it typically requires setting up a request to send messages first, then initiating EventSource. Fetch, like XHR, can carry a request body. Thus, I think Fetch is the better choice.&lt;/li>
&lt;/ol>
&lt;h2 id="chatgpts-effect">
&lt;a class="heading-anchor-link" href="#chatgpts-effect">ChatGPT&amp;rsquo;s Effect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="chatgpts-effect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>ChatGPT has long supported stream returns, which give users the effect of a typewriter. Here is how GPT does it:&lt;/p>
&lt;p>ChatGPT uses &lt;code>Fetch&lt;/code>, send request body, and the return type is &lt;code>text/event-stream; charset=utf-8&lt;/code>, meaning the server continuously returns response chunk until it ends.&lt;/p>
&lt;p>You can find requests can be found in the network, and the request url is &lt;code>https://chat.openai.com/backend-api/conversation&lt;/code>&lt;/p>
&lt;p>done.&lt;/p></description></item><item><title>attachCustomKeyEventHandler in xterm.js</title><link>https://en.1991421.cn/2024/04/13/xtermattachcustomkeyeventhandler/</link><pubDate>Sat, 13 Apr 2024 11:48:29 +0800</pubDate><guid>https://en.1991421.cn/2024/04/13/xtermattachcustomkeyeventhandler/</guid><description>&lt;blockquote>
&lt;p>To handle special key behavior in xterm.js, use &lt;code>attachCustomKeyEventHandler&lt;/code>. Here’s how it works.&lt;/p>
&lt;/blockquote>
&lt;h2 id="source-walkthrough">
&lt;a class="heading-anchor-link" href="#source-walkthrough">Source Walkthrough&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="source-walkthrough"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Attaches a custom key event handler which is run before keys are
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * processed, giving consumers of xterm.js ultimate control as to what keys
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * should be processed by the terminal and what keys should not.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param customKeyEventHandler The custom KeyboardEvent handler to attach.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * This is a function that takes a KeyboardEvent, allowing consumers to stop
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * propagation and/or prevent the default action. The function returns
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * whether the event should be processed by xterm.js.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @example A custom keymap that overrides the backspace key
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * ```ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * const keymap = [
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * { &amp;#34;key&amp;#34;: &amp;#34;Backspace&amp;#34;, &amp;#34;shiftKey&amp;#34;: false, &amp;#34;mapCode&amp;#34;: 8 },
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * { &amp;#34;key&amp;#34;: &amp;#34;Backspace&amp;#34;, &amp;#34;shiftKey&amp;#34;: true, &amp;#34;mapCode&amp;#34;: 127 }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * ];
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * term.attachCustomKeyEventHandler(ev =&amp;gt; {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * if (ev.type === &amp;#39;keydown&amp;#39;) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * for (let i in keymap) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * if (keymap[i].key == ev.key &amp;amp;&amp;amp; keymap[i].shiftKey == ev.shiftKey) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * socket.send(String.fromCharCode(keymap[i].mapCode));
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * return false;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * });
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * ```
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">attachCustomKeyEventHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">customKeyEventHandler&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>: &lt;span class="kt">KeyboardEvent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://github.com/xtermjs/xterm.js/blob/d2b8e6c1251a5664eb9fd82627f0b62349998933/typings/xterm.d.ts#L1013-L1013" target="_blank" rel="noopener">Source location&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">_keyDown&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>: &lt;span class="kt">KeyboardEvent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_keyDownHandled&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_keyDownSeen&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_customKeyEventHandler&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_customKeyEventHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">_keyUp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ev&lt;/span>: &lt;span class="kt">KeyboardEvent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_keyDownSeen&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_customKeyEventHandler&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_customKeyEventHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ev&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">_keyPress&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ev&lt;/span>: &lt;span class="kt">KeyboardEvent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_keyPressHandled&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_keyDownHandled&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_customKeyEventHandler&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_customKeyEventHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ev&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://github.com/xtermjs/xterm.js/blob/d2b8e6c1251a5664eb9fd82627f0b62349998933/src/browser/Terminal.ts#L1001-L1001" target="_blank" rel="noopener">Source location&lt;/a>&lt;/p>
&lt;p>From the above we can see:&lt;/p>
&lt;p>The callback provided to &lt;code>attachCustomKeyEventHandler&lt;/code> runs before xterm.js processes keys. It fires on keydown/keyup/keypress. Returning &lt;code>void&lt;/code> or &lt;code>true&lt;/code> has the same effect; only returning &lt;code>false&lt;/code> prevents xterm.js from further processing the key event.&lt;/p>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Suppose I want `Ctrl + `` in xterm.js to trigger a special action (e.g., invoke AI). Do this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">attachCustomKeyEventHandler&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)=&amp;gt;{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="o">===&lt;/span>&lt;span class="s1">&amp;#39;keydown&amp;#39;&lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="s1">&amp;#39;`&amp;#39;&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">key&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ctrlKey&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// dosomething
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you still want xterm.js to process the key after your handler, return &lt;code>void&lt;/code> or &lt;code>true&lt;/code>.&lt;/p>
&lt;p>Done.&lt;/p></description></item><item><title>Loading ES Modules in Node.js Projects</title><link>https://en.1991421.cn/2024/04/10/f0f8df2/</link><pubDate>Wed, 10 Apr 2024 08:24:01 +0800</pubDate><guid>https://en.1991421.cn/2024/04/10/f0f8df2/</guid><description>&lt;blockquote>
&lt;p>Node.js supports ES module import. You can load ES modules through import, but there are some conditions. If not met, it will still throw errors.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-04-10-101444.jpeg"
alt="https://static.1991421.cn/2024/2024-04-10-101444.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Note: import in Node.js differs from the browser side.&lt;/strong>&lt;/p>
&lt;h2 id="import-local-js-modules">
&lt;a class="heading-anchor-link" href="#import-local-js-modules">Import Local JS Modules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="import-local-js-modules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Declare &lt;code>type:module&lt;/code> in the &lt;code>package.json&lt;/code> of the folder where the js file is located&lt;/li>
&lt;li>File extension should be &lt;code>mjs&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="import-url">
&lt;a class="heading-anchor-link" href="#import-url">Import URL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="import-url"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Executing import(url) in Node throws error &lt;code>ERR_UNSUPPORTED_ESM_URL_SCHEME&lt;/code>&lt;/p>
&lt;p>Error code explanation:&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>import&lt;/code> with URL schemes other than &lt;code>file&lt;/code> and &lt;code>data&lt;/code> is unsupported.&lt;/p>
&lt;/blockquote>
&lt;p>From this, we can know that dynamic import in Node.js doesn&amp;rsquo;t support URLs. However, for example, Deno does support URLs, but Deno doesn&amp;rsquo;t consider CommonJS and doesn&amp;rsquo;t have this historical baggage.&lt;/p></description></item><item><title>What is Shebang? A Guide for Scripting</title><link>https://en.1991421.cn/2024/04/02/shebang/</link><pubDate>Tue, 02 Apr 2024 23:38:50 +0800</pubDate><guid>https://en.1991421.cn/2024/04/02/shebang/</guid><description>&lt;blockquote>
&lt;p>You&amp;rsquo;ve probably seen shell scripts starting with &lt;code>#!/bin/sh&lt;/code>. For a long time, I noticed that some scripts ran fine without it, while others failed. It wasn&amp;rsquo;t until I started developing Alfred Workflows that I realized this line has a specific name and purpose: it&amp;rsquo;s called a &lt;strong>Shebang&lt;/strong>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-04-02-235754.jpeg"
alt="Shebang Illustration"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-is-a-shebang">
&lt;a class="heading-anchor-link" href="#what-is-a-shebang">What is a Shebang?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-a-shebang"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>The Syntax&lt;/strong>: A Shebang consists of the characters &lt;code>#!&lt;/code>. For it to work, these &lt;em>must&lt;/em> be the very first two characters on the first line of the file.&lt;/li>
&lt;li>&lt;strong>The Mechanism&lt;/strong>: When a Unix-like operating system (like macOS or Linux) encounters a file with a Shebang, the program loader parses the rest of the line as an interpreter directive. It then executes that interpreter and passes the script&amp;rsquo;s file path as an argument.&lt;/li>
&lt;li>&lt;strong>Execution vs. Interpretation&lt;/strong>: Without a Shebang, a script cannot be executed directly (e.g., &lt;code>./script.sh&lt;/code>). However, it can still be run if you manually pass it to an interpreter (e.g., &lt;code>bash script.sh&lt;/code>).&lt;/li>
&lt;/ol>
&lt;h2 id="common-examples">
&lt;a class="heading-anchor-link" href="#common-examples">Common Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>&lt;code>#!/bin/sh&lt;/code>&lt;/strong>: Executes the script using &lt;code>sh&lt;/code> (Bourne shell or a compatible replacement).&lt;/li>
&lt;li>&lt;strong>&lt;code>#!/bin/bash&lt;/code>&lt;/strong>: Specifically uses the &lt;code>bash&lt;/code> shell, which often contains features not found in standard &lt;code>sh&lt;/code>.&lt;/li>
&lt;li>&lt;strong>&lt;code>#!/usr/bin/env node&lt;/code>&lt;/strong>: Executes the script using Node.js. By using &lt;code>/usr/bin/env&lt;/code>, the system looks for &lt;code>node&lt;/code> in the user&amp;rsquo;s current &lt;code>$PATH&lt;/code>, which is more portable than hardcoding an absolute path like &lt;code>/usr/local/bin/node&lt;/code>.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="nodejs-script-example">
&lt;a class="heading-anchor-link" href="#nodejs-script-example">Node.js Script Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nodejs-script-example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Consider this simple Node.js script:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="ch">#!/usr/bin/env node
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ch">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Hello from Node!&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">environment&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">USER&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you save this as &lt;code>test.js&lt;/code> and give it execution permissions (&lt;code>chmod +x test.js&lt;/code>), you can run it directly in your terminal with &lt;code>./test.js&lt;/code>. If you were to remove the first line, the system wouldn&amp;rsquo;t know which program to use to run the code, and the execution would fail unless you typed &lt;code>node test.js&lt;/code>.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using a Shebang is a best practice for writing portable scripts. It ensures that your scripts run correctly regardless of how they are invoked or which default shell the user is running.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Shebang_%28Unix%29" target="_blank" rel="noopener">Shebang (Unix) - Wikipedia&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Reading Large Files with FileReader</title><link>https://en.1991421.cn/2024/03/31/filereader/</link><pubDate>Sun, 31 Mar 2024 22:15:36 +0800</pubDate><guid>https://en.1991421.cn/2024/03/31/filereader/</guid><description>&lt;blockquote>
&lt;p>The frontend can read file content with FileReader, but reading large files (1-3 GB) causes noticeable jank. Here are the optimizations.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-31-224554.jpeg"
alt="https://static.1991421.cn/2024/2024-03-31-224554.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="cause-of-jank">
&lt;a class="heading-anchor-link" href="#cause-of-jank">Cause of jank&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cause-of-jank"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you read the entire file at once, all content is loaded into browser memory, which causes jank.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Slice the file and read chunks asynchronously.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">chunkSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1024&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1024&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 1MB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">readChunk&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">chunkSize&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">reader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">FileReader&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onload&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">evt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// console.log(evt.target.result);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">evt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readAsArrayBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">slice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">start&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">chunkSize&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">size&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="comparison">
&lt;a class="heading-anchor-link" href="#comparison">Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using 2.3 GB data as an example:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Using FileReader.readAsArrayBuffer to read in 1 MB chunks, the time cost is below&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-31-223746.jpeg"
alt="https://static.1991421.cn/2024/2024-03-31-223746.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Using FileReader.readAsArrayBuffer to read the full file, it never finishes&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Using 1.98 GB data as an example:&lt;/p>
&lt;ol>
&lt;li>Chunked reading&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-31-223823.jpeg"
alt="https://static.1991421.cn/2024/2024-03-31-223823.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="2">
&lt;li>Full read&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-31-223906.jpeg"
alt="https://static.1991421.cn/2024/2024-03-31-223906.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Chunked reading saves performance and reads specific parts faster, though total time is not necessarily shorter. The larger the file, the bigger the gap.&lt;/li>
&lt;li>Reading files larger than 2 GB in one shot can be problematic in browsers.&lt;/li>
&lt;/ol>
&lt;h2 id="extension">
&lt;a class="heading-anchor-link" href="#extension">Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When handling binary files on the frontend, you often run into Blob and ArrayBuffer. If you only need to slice data, Blob is recommended. Blob is just a view of the original data and does not load it, so for uploads you can slice the file and send chunks directly to the server. But if you need to read or modify file contents in the frontend, ArrayBuffer is recommended.&lt;/p></description></item><item><title>Common PowerShell Commands</title><link>https://en.1991421.cn/2024/03/31/57878870/</link><pubDate>Sun, 31 Mar 2024 11:38:26 +0800</pubDate><guid>https://en.1991421.cn/2024/03/31/57878870/</guid><description>&lt;blockquote>
&lt;p>Recently, WebShell needed to support file management under PowerShell. To perform file-related operations, it&amp;rsquo;s necessary to understand the relevant commands. Here&amp;rsquo;s the summary.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-31-114718.jpeg"
alt="https://static.1991421.cn/2024/2024-03-31-114718.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="compress-folder">
&lt;a class="heading-anchor-link" href="#compress-folder">Compress Folder&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compress-folder"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Compress-Archive&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${formatted}&amp;#39;&lt;/span> &lt;span class="n">-DestinationPath&lt;/span> &lt;span class="s1">&amp;#39;${zipTempPath}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="copy-filefolder">
&lt;a class="heading-anchor-link" href="#copy-filefolder">Copy File/Folder&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="copy-filefolder"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Copy-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${filePath}&amp;#39;&lt;/span> &lt;span class="n">-Destination&lt;/span> &lt;span class="s1">&amp;#39;${dirPath}&amp;#39;&lt;/span> &lt;span class="n">-Recurse&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="move-filefolder">
&lt;a class="heading-anchor-link" href="#move-filefolder">Move File/Folder&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="move-filefolder"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Move-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${filePath}&amp;#39;&lt;/span> &lt;span class="n">-Destination&lt;/span> &lt;span class="s1">&amp;#39;${targetPath}&amp;#39;&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="extract-zip-archive">
&lt;a class="heading-anchor-link" href="#extract-zip-archive">Extract ZIP Archive&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extract-zip-archive"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Expand-Archive&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${path}.zip&amp;#39;&lt;/span> &lt;span class="n">-DestinationPath&lt;/span> &lt;span class="s1">&amp;#39;${destinationPath}&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="nb">Remove-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${path}.zip&amp;#39;&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="delete-filefolder">
&lt;a class="heading-anchor-link" href="#delete-filefolder">Delete File/Folder&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="delete-filefolder"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Remove-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${formatted}&amp;#39;&lt;/span> &lt;span class="n">-Recurse&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="create-folder">
&lt;a class="heading-anchor-link" href="#create-folder">Create Folder&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-folder"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">New-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${formatted}&amp;#39;&lt;/span> &lt;span class="n">-ItemType&lt;/span> &lt;span class="n">Directory&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="rename">
&lt;a class="heading-anchor-link" href="#rename">Rename&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rename"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Rename-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${formatted}&amp;#39;&lt;/span> &lt;span class="n">-NewName&lt;/span> &lt;span class="s1">&amp;#39;${newName}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="multiple-command-execution">
&lt;a class="heading-anchor-link" href="#multiple-command-execution">Multiple Command Execution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="multiple-command-execution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If need to execute multiple commands, unlike using &lt;code>&amp;amp;&amp;amp;&lt;/code> in Linux, PowerShell requires &lt;code>;&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Move-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${path}&amp;#39;&lt;/span> &lt;span class="n">-Destination&lt;/span> &lt;span class="s1">&amp;#39;${path}.zip&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="nb">Expand-Archive&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${path}.zip&amp;#39;&lt;/span> &lt;span class="n">-DestinationPath&lt;/span> &lt;span class="s1">&amp;#39;${destinationPath}&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="nb">Remove-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;${path}.zip&amp;#39;&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="w2-cannot-load-file-cprogram-filesnode-jsw2ps1-because-running-scripts-is-disabled-on-this-system">
&lt;a class="heading-anchor-link" href="#w2-cannot-load-file-cprogram-filesnode-jsw2ps1-because-running-scripts-is-disabled-on-this-system">w2: Cannot load file C:\Program Files\node js\w2.ps1 because running scripts is disabled on this system.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="w2-cannot-load-file-cprogram-filesnode-jsw2ps1-because-running-scripts-is-disabled-on-this-system"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Run PowerShell as administrator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Check the current PowerShell script execution policy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">get-ExecutionPolicy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Set PowerShell&amp;#39;s execution policy to RemoteSigned&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">set-executionpolicy&lt;/span> &lt;span class="n">remotesigned&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="path-contains-square-brackets-">
&lt;a class="heading-anchor-link" href="#path-contains-square-brackets-">Path Contains Square Brackets []&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="path-contains-square-brackets-"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If the path contains [], for example, executing Move-Item doesn’t throw an error but the execution is unsuccessful. The reason is that [] in the path is treated as regex, regardless of whether it’s a single or double quote. There are two solutions:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Escape []&lt;/p>
&lt;p>For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Move-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s1">&amp;#39;C:\\Windows\\Temp\\`[漫威`] xxx.jar_1717312576427&amp;#39;&lt;/span> &lt;span class="n">-Destination&lt;/span> &lt;span class="s1">&amp;#39;C:\\Windows\\system32\\config\\systemprofile\\`[漫威`] xxx.jar&amp;#39;&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Change &lt;code>-Path&lt;/code> to &lt;code>-LiteralPath&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Move-Item&lt;/span> &lt;span class="n">-LiteralPath&lt;/span> &lt;span class="s1">&amp;#39;C:\\Windows\\system32\\config\\systemprofile\\123 456\\[漫威] xxx.jar&amp;#39;&lt;/span> &lt;span class="n">-Destination&lt;/span> &lt;span class="s1">&amp;#39;C:\\Windows\\system32\\config\\systemprofile\\789\\[漫威] xxx.jar&amp;#39;&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When executing Windows commands, you can choose between PowerShell and CMD. According to Microsoft’s recommendation, using PowerShell is advised, as CMD has entered the maintenance phase.&lt;/p>
&lt;h2 id="documentation">
&lt;a class="heading-anchor-link" href="#documentation">Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/21008180/copy-file-with-square-brackets-in-the-filename-and-use-wildcard" target="_blank" rel="noopener">https://stackoverflow.com/questions/21008180/copy-file-with-square-brackets-in-the-filename-and-use-wildcard&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Writing a Welcome Message in xterm.js</title><link>https://en.1991421.cn/2024/03/29/xterm/</link><pubDate>Fri, 29 Mar 2024 09:41:29 +0800</pubDate><guid>https://en.1991421.cn/2024/03/29/xterm/</guid><description>&lt;blockquote>
&lt;p>Terminals often display a welcome message on login. How do you change it? While building a WebShell recently, I considered how to implement this.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-29-125309.jpeg"
alt="https://static.1991421.cn/2024/2024-03-29-125309.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="serverside-configuration">
&lt;a class="heading-anchor-link" href="#serverside-configuration">Server‑side configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="serverside-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The most direct way is server configuration — for example, add &lt;code>echo 'hello world'&lt;/code> to bashrc so it prints after WebShell login.&lt;/p>
&lt;p>Server‑side drawbacks:&lt;/p>
&lt;ol>
&lt;li>Adding a welcome line affects all terminal logins, not just WebShell.&lt;/li>
&lt;li>It requires modifying server config (e.g., editing bashrc), which is heavier.&lt;/li>
&lt;/ol>
&lt;h2 id="xtermjs-clientside-approach">
&lt;a class="heading-anchor-link" href="#xtermjs-clientside-approach">xterm.js client‑side approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="xtermjs-clientside-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Alternatively, implement it on the client with xterm.js. After the terminal connects, run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;\x1B[34;43mmy github is https://github.com/alanhe421 my github is https://github.com/alanhe421 my github is https://github.com/alanhe421 my github is https://github.com/alanhe421\x1B[0m&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;\r\n&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">webshell&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sendData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;resize&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rows&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">rows&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cols&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cols&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Explanation:&lt;/p>
&lt;ol>
&lt;li>&lt;code>term.write&lt;/code> writes directly to the terminal emulator; it is not echoed back from the server.&lt;/li>
&lt;li>Since the server didn’t send this content, it doesn’t know the terminal size. Send &lt;code>rows/cols&lt;/code> to avoid misaligned output in subsequent interactions.&lt;/li>
&lt;/ol></description></item><item><title>Terminal URL Scheme Support</title><link>https://en.1991421.cn/2024/03/21/terminalurl-scheme/</link><pubDate>Thu, 21 Mar 2024 23:45:19 +0800</pubDate><guid>https://en.1991421.cn/2024/03/21/terminalurl-scheme/</guid><description>&lt;blockquote>
&lt;p>Both macOS and Windows ship with terminal apps. If they support URL schemes, you can start an SSH session from a hyperlink. Here’s what works today.&lt;/p>
&lt;/blockquote>
&lt;h2 id="url-schemes">
&lt;a class="heading-anchor-link" href="#url-schemes">URL Schemes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="url-schemes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="macos">
&lt;a class="heading-anchor-link" href="#macos">macOS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="macos"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>ssh://&amp;lt;username&amp;gt;@&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;&lt;/code>&lt;/p>
&lt;p>&lt;a href="ssh://root@38.54.17.20:22">Click to test&lt;/a>&lt;/p>
&lt;p>By default this opens Terminal, but you can remap it to iTerm2 or another terminal app via settings.&lt;/p>
&lt;h3 id="windows">
&lt;a class="heading-anchor-link" href="#windows">Windows&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="windows"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>No built-in support. You can configure PuTTY or other tools to handle &lt;code>ssh://&lt;/code> (e.g., &lt;a href="https://gist.github.com/sbiffi/11256316%29" target="_blank" rel="noopener">https://gist.github.com/sbiffi/11256316)&lt;/a>.&lt;/p>
&lt;p>Windows does have other built-in schemes, such as launching the Store to a product page: &lt;a href="ms-windows-store://pdp/?ProductId=9N0DX20HK701">test link&lt;/a>. Also note that Windows 11 ships with Windows Terminal preinstalled; older versions require manual installation.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It’d be great if Windows added native terminal URL handling—macOS has a clear edge here.&lt;/p></description></item><item><title>Submitting PRs to GitHub Open Source Projects</title><link>https://en.1991421.cn/2024/03/17/githubpr/</link><pubDate>Sun, 17 Mar 2024 12:28:32 +0800</pubDate><guid>https://en.1991421.cn/2024/03/17/githubpr/</guid><description>&lt;blockquote>
&lt;p>Working on your own projects on GitHub is relatively simple, but how to collaborate and contribute to open source projects is more interesting. Here I&amp;rsquo;m documenting the experience I&amp;rsquo;ve accumulated contributing to open source projects like inshellisense.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-17-144331.png"
alt="https://static.1991421.cn/2024/2024-03-17-144331.png"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Using the inshellisense project as an example&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Fork the open source project, &lt;code>microsoft/inshellisense&lt;/code> =&amp;gt; &lt;code>alanhg/inshellisense&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Create a new branch, such as &lt;code>fix/file-template&lt;/code>, for development&lt;/p>
&lt;ul>
&lt;li>Update unit tests if available, ensure tests pass&lt;/li>
&lt;li>Don&amp;rsquo;t use the main branch directly because the main branch is used to sync project updates, avoiding situations where the original project gets new commits during development&lt;/li>
&lt;li>Creating a new branch also allows better use of branch names to indicate whether the changes are features, fixes, etc.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Commit to the upstream repository &lt;code>alanhg/inshellisense&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If the open source project &lt;code>microsoft/inshellisense&lt;/code> has updates, pull the updates using &lt;code>Sync fork&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Rebase the &lt;code>fix/file-template&lt;/code> branch with the main branch to ensure it&amp;rsquo;s up to date&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Push again to the upstream repository &lt;code>alanhg/inshellisense&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Initiate a &lt;code>Pull Request&lt;/code>, describe the changes in detail, and wait for merging&lt;/p>
&lt;ul>
&lt;li>If there are any issues during PR review, provide active feedback and resolve them&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Once successfully merged, celebrate 🎉&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You can delete the &lt;code>fix/file-template&lt;/code> branch, switch to the main branch, and if there are new contributions to make, repeat step 5 for operation&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Debugging Local Packages with Pnpm and Vite: Common Issues and Solutions</title><link>https://en.1991421.cn/2024/03/03/pnpmvite/</link><pubDate>Sun, 03 Mar 2024 23:37:54 +0800</pubDate><guid>https://en.1991421.cn/2024/03/03/pnpmvite/</guid><description>&lt;blockquote>
&lt;p>When developing with Pnpm + Vite, during local package debugging, Vite may still load the old version of the package. This article analyzes and resolves this issue.&lt;/p>
&lt;/blockquote>
&lt;h2 id="local-package-debugging-steps">
&lt;a class="heading-anchor-link" href="#local-package-debugging-steps">Local Package Debugging Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="local-package-debugging-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since the package manager is pnpm, the corresponding debugging package mounting steps are as follows:&lt;/p>
&lt;ol>
&lt;li>Execute &lt;code>pnpm link --global&lt;/code> in the local package&lt;/li>
&lt;li>Execute &lt;code>pnpm link pkgname --global&lt;/code> in the target project&lt;/li>
&lt;/ol>
&lt;p>After performing these steps, accessing the web browser will reproduce the issue of incorrect package versions.&lt;/p>
&lt;p>Let&amp;rsquo;s first analyze the situation in vite/pnpm&lt;/p>
&lt;h2 id="vite">
&lt;a class="heading-anchor-link" href="#vite">.vite&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="vite"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When Vite builds a project, it creates a &lt;code>.vite&lt;/code> folder under &lt;code>node_modules&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-03-03-233708.jpeg"
alt="https://static.1991421.cn/2024/2024-03-03-233708.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The &lt;code>.vite&lt;/code> cache requires changes in any of the following conditions:&lt;/p>
&lt;ul>
&lt;li>Package manager lockfile content, e.g. &lt;code>package-lock.json&lt;/code>, &lt;code>yarn.lock&lt;/code>, &lt;code>pnpm-lock.yaml&lt;/code> or &lt;code>bun.lockb&lt;/code>.&lt;/li>
&lt;li>Patches folder modification time.&lt;/li>
&lt;li>Relevant fields in your &lt;code>vite.config.js&lt;/code>, if present.&lt;/li>
&lt;li>&lt;code>NODE_ENV&lt;/code> value.&lt;/li>
&lt;/ul>
&lt;p>To force a refresh in Vite, you need to use &lt;code>--force&lt;/code> or delete the &lt;code>.vite&lt;/code> directory. For more details, see the &lt;a href="https://vitejs.dev/guide/dep-pre-bundling.html#file-system-cache" target="_blank" rel="noopener">official documentation&lt;/a>&lt;/p>
&lt;p>When executing &lt;code>pnpm link&lt;/code>, you&amp;rsquo;ll notice that the lock file doesn&amp;rsquo;t change, so the &lt;code>.vite&lt;/code> cache won&amp;rsquo;t be updated.&lt;/p>
&lt;h2 id="pnpm-directory">
&lt;a class="heading-anchor-link" href="#pnpm-directory">.pnpm Directory&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pnpm-directory"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Packages in node_modules are soft-linked to the &lt;code>.pnpm&lt;/code> folder. When executing pnpm link:&lt;/p>
&lt;ol>
&lt;li>The lock file won&amp;rsquo;t change&lt;/li>
&lt;li>The corresponding package in node_modules will be soft-linked to the globally linked package&lt;/li>
&lt;/ol>
&lt;h2 id="problem-analysis">
&lt;a class="heading-anchor-link" href="#problem-analysis">Problem Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In summary, the issue is that after pnpm link, the lock file doesn&amp;rsquo;t get updated. Although the package dependency links change (meaning the content changes), Vite is unaware of this. Therefore, the Vite cache needs to be manually refreshed. Manually deleting the .vite directory and restarting Vite will show that the correct content is loaded.&lt;/p>
&lt;h2 id="related-issues">
&lt;a class="heading-anchor-link" href="#related-issues">Related Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="sourcemap">
&lt;a class="heading-anchor-link" href="#sourcemap">SourceMap&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sourcemap"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>SourceMap is designed to facilitate debugging with source code, such as being able to search for source files in Chrome Network. The reason it can be retrieved is that when the browser has Network open, it attempts to load the corresponding map file based on the sourcemap address noted in the code file. The map file records the location of the source files, and then the source code files are loaded. If you can&amp;rsquo;t retrieve source code files in Chrome, you can check whether the map files and source files are being loaded successfully with the correct paths.&lt;/p>
&lt;h3 id="sourcemap-not-working-when-debugging-webpack-packages-in-vite">
&lt;a class="heading-anchor-link" href="#sourcemap-not-working-when-debugging-webpack-packages-in-vite">SourceMap Not Working When Debugging Webpack Packages in Vite&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sourcemap-not-working-when-debugging-webpack-packages-in-vite"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The reason is that the sourcemap generated by Webpack uses the webpack protocol for source files, which Vite doesn&amp;rsquo;t support. One solution is to switch to Vite for building.&lt;/p>
&lt;p>![image-20240305231617186](/Users/alanhe/Library/Mobile Documents/com&lt;del>apple&lt;/del>CloudDocs/Typora/image-20240305231617186.png)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;file&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;index.js&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;mappings&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;AAAA&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;sources&amp;#34;&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="s2">&amp;#34;webpack://laputarenderer/webpack/universalModuleDefinition&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="s2">&amp;#34;sourcesContent&amp;#34;&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="s2">&amp;#34;(function webpackUniversalModuleDefinition(root, factory) {&lt;/span>&lt;span class="se">\n\t&lt;/span>&lt;span class="s2">if(typeof exports === &amp;#39;object&amp;#39; &amp;amp;&amp;amp; typeof module === &amp;#39;object&amp;#39;)&lt;/span>&lt;span class="se">\n\t\t&lt;/span>&lt;span class="s2">module.exports = factory(require(&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">react&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">));&lt;/span>&lt;span class="se">\n\t&lt;/span>&lt;span class="s2">else if(typeof define === &amp;#39;function&amp;#39; &amp;amp;&amp;amp; define.amd)&lt;/span>&lt;span class="se">\n\t\t&lt;/span>&lt;span class="s2">define([&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">react&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">], factory);&lt;/span>&lt;span class="se">\n\t&lt;/span>&lt;span class="s2">else if(typeof exports === &amp;#39;object&amp;#39;)&lt;/span>&lt;span class="se">\n\t\t&lt;/span>&lt;span class="s2">exports[&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">laputarenderer&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">] = factory(require(&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">react&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">));&lt;/span>&lt;span class="se">\n\t&lt;/span>&lt;span class="s2">else&lt;/span>&lt;span class="se">\n\t\t&lt;/span>&lt;span class="s2">root[&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">laputarenderer&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">] = factory(root[&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">React&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">]);&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">})(self, (__WEBPACK_EXTERNAL_MODULE__12__) =&amp;gt; {&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">return &amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="s2">&amp;#34;names&amp;#34;&lt;/span>&lt;span class="p">:[],&lt;/span>&lt;span class="s2">&amp;#34;sourceRoot&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://pnpm.io/cli/link" target="_blank" rel="noopener">https://pnpm.io/cli/link&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://vitejs.dev/guide/dep-pre-bundling.html#file-system-cache" target="_blank" rel="noopener">https://vitejs.dev/guide/dep-pre-bundling.html#file-system-cache&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Debugging local packages with Pnpm and Vite can be challenging due to caching mechanisms. The key insight is understanding that Vite&amp;rsquo;s dependency pre-bundling relies on lockfile changes to invalidate its cache. When using &lt;code>pnpm link&lt;/code>, the lockfile remains unchanged, causing Vite to continue using cached versions of packages. The solution is to manually clear the Vite cache by deleting the &lt;code>.vite&lt;/code> directory or using the &lt;code>--force&lt;/code> flag when restarting the development server. This approach ensures that your local package changes are properly reflected during development.&lt;/p></description></item><item><title>JavaScript String trim() Method</title><link>https://en.1991421.cn/2024/03/02/jsstr-trim/</link><pubDate>Sat, 02 Mar 2024 22:25:08 +0800</pubDate><guid>https://en.1991421.cn/2024/03/02/jsstr-trim/</guid><description>&lt;blockquote>
&lt;p>String objects have a trim method, but trim doesn&amp;rsquo;t just handle leading and trailing spaces. I recently discovered I had a misconception about this when using it, so I&amp;rsquo;m making a note here.&lt;/p>
&lt;/blockquote>
&lt;p>According to the MDN introduction, trim handles whitespace + line terminators at the beginning and end:&lt;/p>
&lt;blockquote>
&lt;p>A new string representing &lt;code>str&lt;/code> stripped of whitespace from both its beginning and end. Whitespace is defined as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#white_space" target="_blank" rel="noopener">white space&lt;/a> characters plus &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#line_terminators" target="_blank" rel="noopener">line terminators&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>Space is just one type of white space. So what specific characters are included?&lt;/p>
&lt;h2 id="white-spacehttpsdevelopermozillaorgen-usdocswebjavascriptreferencelexical_grammarwhite_space">
&lt;a class="heading-anchor-link" href="#white-spacehttpsdevelopermozillaorgen-usdocswebjavascriptreferencelexical_grammarwhite_space">&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#white_space" target="_blank" rel="noopener">White space&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="white-spacehttpsdevelopermozillaorgen-usdocswebjavascriptreferencelexical_grammarwhite_space"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Code point&lt;/td>
&lt;td style="text-align: left">Name&lt;/td>
&lt;td style="text-align: left">Abbreviation&lt;/td>
&lt;td style="text-align: left">Description&lt;/td>
&lt;td style="text-align: left">Escape sequence&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+0009&lt;/td>
&lt;td style="text-align: left">Character tabulation&lt;/td>
&lt;td style="text-align: left">&lt;TAB>&lt;/td>
&lt;td style="text-align: left">Horizontal tabulation&lt;/td>
&lt;td style="text-align: left">\t&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+000B&lt;/td>
&lt;td style="text-align: left">Line tabulation&lt;/td>
&lt;td style="text-align: left">&lt;VT>&lt;/td>
&lt;td style="text-align: left">Vertical tabulation&lt;/td>
&lt;td style="text-align: left">\v&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+000C&lt;/td>
&lt;td style="text-align: left">Form feed&lt;/td>
&lt;td style="text-align: left">&lt;FF>&lt;/td>
&lt;td style="text-align: left">Page breaking control character (&lt;a href="https://en.wikipedia.org/wiki/Page_break#Form_feed" target="_blank" rel="noopener">Wikipedia&lt;/a>).&lt;/td>
&lt;td style="text-align: left">\f&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+0020&lt;/td>
&lt;td style="text-align: left">Space&lt;/td>
&lt;td style="text-align: left">&lt;SP>&lt;/td>
&lt;td style="text-align: left">Normal space&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+00A0&lt;/td>
&lt;td style="text-align: left">No-break space&lt;/td>
&lt;td style="text-align: left">&lt;NBSP>&lt;/td>
&lt;td style="text-align: left">Normal space, but no point at which a line may break&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+FEFF&lt;/td>
&lt;td style="text-align: left">Zero-width no-break space&lt;/td>
&lt;td style="text-align: left">&lt;ZWNBSP>&lt;/td>
&lt;td style="text-align: left">When not at the start of a script, the BOM marker is a normal whitespace character.&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Others&lt;/td>
&lt;td style="text-align: left">Other Unicode space characters&lt;/td>
&lt;td style="text-align: left">&lt;USP>&lt;/td>
&lt;td style="text-align: left">&lt;a href="https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5cp%7bGeneral_Category%3DSpace_Separator%7d" target="_blank" rel="noopener">Characters in the &amp;ldquo;Space_Separator&amp;rdquo; general category&lt;/a>&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="line-terminatorshttpsdevelopermozillaorgen-usdocswebjavascriptreferencelexical_grammarline_terminators">
&lt;a class="heading-anchor-link" href="#line-terminatorshttpsdevelopermozillaorgen-usdocswebjavascriptreferencelexical_grammarline_terminators">&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#line_terminators" target="_blank" rel="noopener">Line terminators&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="line-terminatorshttpsdevelopermozillaorgen-usdocswebjavascriptreferencelexical_grammarline_terminators"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;th style="text-align: left">&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Code point&lt;/td>
&lt;td style="text-align: left">Name&lt;/td>
&lt;td style="text-align: left">Abbreviation&lt;/td>
&lt;td style="text-align: left">Description&lt;/td>
&lt;td style="text-align: left">Escape sequence&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+000A&lt;/td>
&lt;td style="text-align: left">Line Feed&lt;/td>
&lt;td style="text-align: left">&lt;LF>&lt;/td>
&lt;td style="text-align: left">New line character in UNIX systems.&lt;/td>
&lt;td style="text-align: left">\n&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+000D&lt;/td>
&lt;td style="text-align: left">Carriage Return&lt;/td>
&lt;td style="text-align: left">&lt;CR>&lt;/td>
&lt;td style="text-align: left">New line character in Commodore and early Mac systems.&lt;/td>
&lt;td style="text-align: left">\r&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+2028&lt;/td>
&lt;td style="text-align: left">Line Separator&lt;/td>
&lt;td style="text-align: left">&lt;LS>&lt;/td>
&lt;td style="text-align: left">&lt;a href="https://en.wikipedia.org/wiki/Newline" target="_blank" rel="noopener">Wikipedia&lt;/a>&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">U+2029&lt;/td>
&lt;td style="text-align: left">Paragraph Separator&lt;/td>
&lt;td style="text-align: left">&lt;PS>&lt;/td>
&lt;td style="text-align: left">&lt;a href="https://en.wikipedia.org/wiki/Newline" target="_blank" rel="noopener">Wikipedia&lt;/a>&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>So strings containing any of the above characters at the beginning or end will have them removed after trim().&lt;/p>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s test a few characters:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\r123\r&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\n123\n&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39; \n123\n&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 3
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="handling-only-spaces">
&lt;a class="heading-anchor-link" href="#handling-only-spaces">Handling Only Spaces&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-only-spaces"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39; \n123\n&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/^\x20+|\x20+$/g&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I discovered this issue because in my program, a string was assigned the value \r, and after str.trim() it became an empty string, which didn&amp;rsquo;t meet expectations. This prompted me to revisit the trim logic and realize my understanding was incorrect.&lt;/p></description></item><item><title>How to Use HTTP3 (Step-by-Step Guide)</title><link>https://en.1991421.cn/2024/03/02/http3/</link><pubDate>Sat, 02 Mar 2024 16:57:17 +0800</pubDate><guid>https://en.1991421.cn/2024/03/02/http3/</guid><description>&lt;blockquote>
&lt;p>Recently switched my blog server from Nginx to Caddy, and since Caddy supports HTTP3, I decided to try upgrading to HTTP3.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>Note: On June 6, 2022, Robin Mark, a member of the IETF QUIC and HTTP working groups, announced on Twitter that after 5 years, HTTP/3 has finally been standardized as RFC 9114, making it the third major version of the HTTP hypertext transfer protocol.&lt;/em>&lt;/p>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="caddy">
&lt;a class="heading-anchor-link" href="#caddy">Caddy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="caddy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> servers {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> protocols h1 h2 h2c h3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Execute the command to check port status: &lt;code>netstat -tulpn | grep caddy&lt;/code>&lt;/p>
&lt;p>UDP ports do not require listening&lt;/p>
&lt;h3 id="port-opening">
&lt;a class="heading-anchor-link" href="#port-opening">Port Opening&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="port-opening"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Ensure the firewall allows port 443/UDP&lt;/p>
&lt;h3 id="clientbrowserchrome">
&lt;a class="heading-anchor-link" href="#clientbrowserchrome">Client/Browser/Chrome&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="clientbrowserchrome"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In addition to server-side support, the client also needs to ensure it&amp;rsquo;s enabled. For Chrome, newer versions like &lt;code>v122.0.6261.69&lt;/code> have QUIC support enabled by default.&lt;/p>
&lt;p>To confirm the enabled status, check: &lt;a href="chrome://flags/#enable-quic">&lt;code>chrome://flags/#enable-quic&lt;/code>&lt;/a>&lt;/p>
&lt;h2 id="testing-if-a-site-supports-http3">
&lt;a class="heading-anchor-link" href="#testing-if-a-site-supports-http3">Testing if a Site Supports HTTP3&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing-if-a-site-supports-http3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://http3check.net/" target="_blank" rel="noopener">https://http3check.net/&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Execute the command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker run --rm ymuski/curl-http3 curl -IL --http3 https://1991421.cn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://www.lambdatest.com/web-technologies/http3" target="_blank" rel="noopener">https://www.lambdatest.com/web-technologies/http3&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://kb.nssurge.com/surge-knowledge-base/v/zh/technotes/http-protocol-version" target="_blank" rel="noopener">https://kb.nssurge.com/surge-knowledge-base/v/zh/technotes/http-protocol-version&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://gist.github.com/xmlking/cff9510dac9281d29390392cbbb033a8" target="_blank" rel="noopener">https://gist.github.com/xmlking/cff9510dac9281d29390392cbbb033a8&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>HTTP3 represents a significant advancement in web protocols, offering improved performance and reliability over previous HTTP versions. The transition from TCP to QUIC as the underlying transport protocol brings benefits like reduced connection establishment time, improved multiplexing, and better performance on lossy networks. While adoption is still growing, enabling HTTP3 on your server can provide a better experience for users with compatible browsers and clients.&lt;/p></description></item><item><title>$TERM Explained (Simple Guide)</title><link>https://en.1991421.cn/2024/02/25/term/</link><pubDate>Sun, 25 Feb 2024 10:15:03 +0800</pubDate><guid>https://en.1991421.cn/2024/02/25/term/</guid><description>&lt;blockquote>
&lt;p>While building a WebShell I kept seeing scripts reference &lt;code>$TERM&lt;/code>. Here’s a quick primer on what it does and why it matters.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-02-25-225930.jpeg"
alt="https://static.1991421.cn/2024/2024-02-25-225930.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-is-term">
&lt;a class="heading-anchor-link" href="#what-is-term">What Is $TERM?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-term"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;code>$TERM&lt;/code> is an environment variable in Unix-like shells that defines the terminal type. It tells programs what capabilities (colors, cursor control, etc.) to expect.&lt;/p>
&lt;/blockquote>
&lt;p>Common values include:&lt;/p>
&lt;ul>
&lt;li>&lt;code>xterm&lt;/code>&lt;/li>
&lt;li>&lt;code>xterm-color&lt;/code>&lt;/li>
&lt;li>&lt;code>xterm-256color&lt;/code>&lt;/li>
&lt;li>&lt;code>vt220&lt;/code>&lt;/li>
&lt;li>&lt;code>vt100&lt;/code>&lt;/li>
&lt;li>&lt;code>dumb&lt;/code>&lt;/li>
&lt;li>&lt;code>linux&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Interactive SSH sessions usually report &lt;code>xterm-256color&lt;/code>; non-interactive sessions often report &lt;code>dumb&lt;/code> or nothing at all, which is why those sessions lack color.&lt;/p>
&lt;h2 id="setting-the-value">
&lt;a class="heading-anchor-link" href="#setting-the-value">Setting the Value&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setting-the-value"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>You can set &lt;code>$TERM&lt;/code> in shell startup files (&lt;code>~/.bashrc&lt;/code>, &lt;code>~/.bash_profile&lt;/code>, etc.).&lt;/li>
&lt;li>SSH clients set it when opening sessions. For instance, the &lt;code>ssh2&lt;/code> client library lets you configure pseudo-TTY settings.&lt;/li>
&lt;/ol>
&lt;h2 id="quick-test">
&lt;a class="heading-anchor-link" href="#quick-test">Quick Test&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="quick-test"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>On Ubuntu, changing &lt;code>$TERM&lt;/code> in &lt;code>.bashrc&lt;/code> to &lt;code>vt100&lt;/code> removes color; &lt;code>xterm-256color&lt;/code> restores it.&lt;/p>
&lt;h2 id="takeaway">
&lt;a class="heading-anchor-link" href="#takeaway">Takeaway&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeaway"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You usually don’t need to set &lt;code>$TERM&lt;/code> manually—terminal emulators handle it. But now you know what it does if you ever need to override it.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://bash.cyberciti.biz/guide/$TERM_variable" target="_blank" rel="noopener">https://bash.cyberciti.biz/guide/$TERM_variable&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/mscdex/ssh2?tab=readme-ov-file#pseudo-tty-settings" target="_blank" rel="noopener">https://github.com/mscdex/ssh2?tab=readme-ov-file#pseudo-tty-settings&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use GitHub Copilot CLI (Step-by-Step Guide)</title><link>https://en.1991421.cn/2024/02/08/github-copilot-cli/</link><pubDate>Thu, 08 Feb 2024 23:09:45 +0800</pubDate><guid>https://en.1991421.cn/2024/02/08/github-copilot-cli/</guid><description>&lt;blockquote>
&lt;p>Copilot CLI is now in public beta. I hadn&amp;rsquo;t tried it before, but now I&amp;rsquo;ve given it a shot. Here&amp;rsquo;s how to use it and my thoughts on it.&lt;/p>
&lt;/blockquote>
&lt;p>Current CLI version: &lt;code>version 0.5.4-beta (2024-01-04)&lt;/code>&lt;/p>
&lt;h2 id="requirements">
&lt;a class="heading-anchor-link" href="#requirements">Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Like GitHub Copilot, requires a paid subscription&lt;/p>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install GitHub CLI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install gh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Update GitHub CLI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew upgrade gh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Login to GitHub&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gh auth login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install copilot&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gh extension install github/gh-copilot
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After the above installation, you can use it normally&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Currently, the CLI functionality is quite simple:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Explain commands&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">gh copilot explain &amp;#34;git push&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Suggest commands&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">gh copilot suggest &amp;#34;install git&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Currently, you cannot set custom prompts, so the returned results will be in English, but command prompts can be described in Chinese since the underlying OpenAI supports multiple languages&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-02-08-231521.jpeg"
alt="https://static.1991421.cn/2024/2024-02-08-231521.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>That&amp;rsquo;s all there is to the CLI functionality. Looking forward to continued CLI updates, such as supporting system prompts or supporting chat rather than being limited to commands.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.github.com/en/copilot/github-copilot-in-the-cli/using-github-copilot-in-the-cli" target="_blank" rel="noopener">https://docs.github.com/en/copilot/github-copilot-in-the-cli/using-github-copilot-in-the-cli&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Great Plugins for Hexo</title><link>https://en.1991421.cn/2024/02/08/hexo/</link><pubDate>Thu, 08 Feb 2024 10:43:35 +0800</pubDate><guid>https://en.1991421.cn/2024/02/08/hexo/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;m still using Hexo for my blog. The following are the hexo plugins I used.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-02-08-112340.jpeg"
alt="https://static.1991421.cn/2024/2024-02-08-112340.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: The configuration file is &lt;code>_config.yml&lt;/code>&lt;/p>
&lt;h2 id="hexo-abbrlink">
&lt;a class="heading-anchor-link" href="#hexo-abbrlink">hexo-abbrlink&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-abbrlink"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">permalink: :year/:month/:day/:abbrlink/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-algoliasearch">
&lt;a class="heading-anchor-link" href="#hexo-algoliasearch">hexo-algoliasearch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-algoliasearch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Article search and free quota are enough for me.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://github.com/LouisBarranqueiro/hexo-algoliasearch&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">algolia&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">appId&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">applicationID&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">apiKey&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">adminApiKey&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">chunkSize&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">5000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">indexName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my_blog&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fields&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">content:strip:truncate,0,200&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">title&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">permalink&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">tags&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-excerpt">
&lt;a class="heading-anchor-link" href="#hexo-excerpt">hexo-excerpt&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-excerpt"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Automatic excerpt&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://github.com/chekun/hexo-excerpt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">excerpt&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">excerpt_excludes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;img&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">more_excludes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hideWholePostExcerpts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-generator-archive">
&lt;a class="heading-anchor-link" href="#hexo-generator-archive">hexo-generator-archive&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-generator-archive"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Archive page generation&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://github.com/hexojs/hexo-generator-archive&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">archive_generator&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">per_page&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">yearly&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">monthly&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">daily&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">order_by&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>-&lt;span class="l">date&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-generator-feed">
&lt;a class="heading-anchor-link" href="#hexo-generator-feed">hexo-generator-feed&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-generator-feed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Generate feed.xml subscription link, serving RSS&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://github.com/hexojs/hexo-generator-feed&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">feed&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">atom&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">atom.xml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">20&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hub&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">content&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">content_limit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">140&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">content_limit_delim&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-generator-sitemap">
&lt;a class="heading-anchor-link" href="#hexo-generator-sitemap">hexo-generator-sitemap&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-generator-sitemap"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Generate site XML, it&amp;rsquo;s useful for SEO&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Generate sitemap.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># https://github.com/hexojs/hexo-generator-sitemap&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">sitemap&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sitemap.xml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-generator-tag">
&lt;a class="heading-anchor-link" href="#hexo-generator-tag">hexo-generator-tag&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-generator-tag"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Generate tag label page&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://github.com/hexojs/hexo-generator-tag&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">tag_generator&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">per_page&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">order_by&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>-&lt;span class="l">date&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-symbols-count-time">
&lt;a class="heading-anchor-link" href="#hexo-symbols-count-time">hexo-symbols-count-time&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-symbols-count-time"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Generate article statistics, such as word count, estimated reading time, etc.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># @see https://github.com/theme-next/hexo-symbols-count-time&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">symbols_count_time&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">symbols&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">time&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">total_symbols&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">total_time&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">exclude_codeblock&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">awl&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">wpm&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">300&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="hexo-theme-next">
&lt;a class="heading-anchor-link" href="#hexo-theme-next">hexo-theme-next&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hexo-theme-next"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Site theme, I prefer theme-next, mainly because it has quite rich configurations&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># https://github.com/next-theme/hexo-theme-next&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">theme&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">next&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="write-at-the-end">
&lt;a class="heading-anchor-link" href="#write-at-the-end">Write at the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="write-at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Hexo is still in an active state, don&amp;rsquo;t worry about it.&lt;/li>
&lt;li>The main focus of blog maintenance is still on content, currently, Hexo is enough.&lt;/li>
&lt;/ol></description></item><item><title>An Introduction to the ssh2 Package</title><link>https://en.1991421.cn/2024/01/14/ssh2/</link><pubDate>Sun, 14 Jan 2024 10:31:13 +0800</pubDate><guid>https://en.1991421.cn/2024/01/14/ssh2/</guid><description>&lt;blockquote>
&lt;p>When building a WebShell, the &lt;code>ssh2&lt;/code> package often comes in handy. Here’s a brief guide to using it.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Note: The ssh2 module is a Node.js client implementation of the OpenSSH protocol.&lt;/strong>&lt;/p>
&lt;h2 id="common-apis">
&lt;a class="heading-anchor-link" href="#common-apis">Common APIs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-apis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Client&lt;/p>
&lt;ol>
&lt;li>connect — establish a connection&lt;/li>
&lt;/ol>
&lt;p>Supports password/key/keyboard-interactive auth. Once connected, you can perform basic operations.&lt;/p>
&lt;p>If connection fails, enable debug logs to troubleshoot. In general, if your local terminal can connect, ssh2 can as well—they are both OpenSSH clients.&lt;/p>
&lt;ol start="2">
&lt;li>con.shell&lt;/li>
&lt;/ol>
&lt;p>Open an interactive shell, just like a normal terminal session with continuous input/output. You can disconnect in various ways (disconnect, idle timeout, typing &lt;code>exit&lt;/code>, etc.).&lt;/p>
&lt;ol start="3">
&lt;li>con.exec&lt;/li>
&lt;/ol>
&lt;p>Execute a single command (e.g., &lt;code>cd&lt;/code>). After execution completes, the stream ends (&lt;code>stream.end&lt;/code>).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>httpAgent&lt;/p>
&lt;p>The agent enables tunneling into internal networks. For example, if the target machine hosts a web service on &lt;code>127.0.0.1:80&lt;/code>, you can reach it through an SSH-established agent.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>sftp&lt;/p>
&lt;p>For file operations, I recommend the &lt;code>ssh2-sftp-client&lt;/code> package built on top of ssh2.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Server&lt;/p>
&lt;p>Create an SSH server.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>When using ssh2, you’ll come across the concept of Channels — here’s a note on that.&lt;/p>
&lt;h2 id="channel">
&lt;a class="heading-anchor-link" href="#channel">Channel&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="channel"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>A &lt;code>connect&lt;/code> call establishes a single SSH connection, which runs over TCP. Since TCP supports multiplexing, ssh2 can run different operations over the same SSH connection.&lt;/p>
&lt;/blockquote>
&lt;p>Interactive shell, SFTP, and &lt;code>exec&lt;/code> commands each open a separate channel; they are isolated from one another. For example, if you &lt;code>sudo&lt;/code> in an interactive shell, SFTP/exec channels are unaware. You can choose to reuse one SSH connection (multiple channels) or not — using different &lt;code>Client&lt;/code> instances creates separate connections.&lt;/p>
&lt;p>On the target machine, you can check SSH connection counts with &lt;code>netstat -an | grep ':22'&lt;/code>.&lt;/p>
&lt;h2 id="some-questions">
&lt;a class="heading-anchor-link" href="#some-questions">Some Questions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="some-questions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="is-cwd-controllable">
&lt;a class="heading-anchor-link" href="#is-cwd-controllable">Is cwd controllable?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-cwd-controllable"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>OpenSSH does not support this, so ssh2 doesn’t either. You cannot open a &lt;code>.shell&lt;/code> session in a specified CWD directly. The only workaround is to send &lt;code>stream.write(\&lt;/code>cd /var/www\n`)` after starting the shell.&lt;/p>
&lt;p>The downside is that your input will be echoed.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s it for the ssh2 basics.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/mscdex/ssh2" target="_blank" rel="noopener">https://github.com/mscdex/ssh2&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/mscdex/ssh2/issues/813" target="_blank" rel="noopener">https://github.com/mscdex/ssh2/issues/813&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Introducing xterm-headless</title><link>https://en.1991421.cn/2023/12/25/ece90a3/</link><pubDate>Mon, 25 Dec 2023 22:47:35 +0800</pubDate><guid>https://en.1991421.cn/2023/12/25/ece90a3/</guid><description>&lt;blockquote>
&lt;p>In addition to browser components/addons, the official xterm.js repo also provides a headless component. So what is xterm‑headless?&lt;/p>
&lt;p>While browsing inshellisense recently I noticed it uses xterm‑headless. After digging in, the role of the headless component became clear. Notes below.&lt;/p>
&lt;/blockquote>
&lt;h2 id="headless-nodejs-support">
&lt;a class="heading-anchor-link" href="#headless-nodejs-support">headless nodejs support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="headless-nodejs-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>From the &lt;a href="https://github.com/xtermjs/xterm.js?tab=readme-ov-file#nodejs-support" target="_blank" rel="noopener">official docs&lt;/a>, headless is for Node.js usage. In other words, the server can instantiate a terminal client component and operate on it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-25-225330.jpeg"
alt="https://static.1991421.cn/2023/2023-12-25-225330.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Notes&lt;/p>
&lt;ol>
&lt;li>There are no official Node‑side addons yet, but custom ones are possible — just don’t depend on DOM APIs.&lt;/li>
&lt;/ol>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Usage mirrors the browser side — instantiate &lt;code>xterm.Terminal&lt;/code> directly.&lt;/p>
&lt;h2 id="use-cases">
&lt;a class="heading-anchor-link" href="#use-cases">Use cases&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-cases"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Knowing the server can also host xterm.js, where does it shine?&lt;/p>
&lt;p>For example: with an ssh2 connection on the server, data exchanged with the target host is a stream. Typically, we forward it to the client and write it into xterm.js, which structures it. With server‑side headless support, we can also feed the data into a server‑resident terminal. If the user closes the browser, the server session keeps running and can continue to write data to the “virtual” terminal — essentially a remote/sentinel mode.&lt;/p>
&lt;h2 id="inshellisense">
&lt;a class="heading-anchor-link" href="#inshellisense">inshellisense&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="inshellisense"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Back to inshellisense (IS): it uses headless to write terminal data into a virtual terminal so it can compute cursor positions, extract terminal info, etc. See the &lt;a href="https://github.com/microsoft/inshellisense" target="_blank" rel="noopener">source&lt;/a>.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Headless is quite useful — the server can fully instantiate an xterm instance.&lt;/p></description></item><item><title>How to Use Tampermonkey (Step-by-Step Guide)</title><link>https://en.1991421.cn/2023/12/23/tampermonkey-usage/</link><pubDate>Sat, 23 Dec 2023 23:16:35 +0800</pubDate><guid>https://en.1991421.cn/2023/12/23/tampermonkey-usage/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;ve been using Tampermonkey scripts more this year. Here&amp;rsquo;s a summary of my current usage.&lt;/p>
&lt;/blockquote>
&lt;h2 id="purpose">
&lt;a class="heading-anchor-link" href="#purpose">Purpose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="purpose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Tampermonkey is used to enhance web pages, such as supporting video playback at multiple speeds, modifying page styles, automating some operations, etc.&lt;/p>
&lt;h2 id="common-scripts">
&lt;a class="heading-anchor-link" href="#common-scripts">Common scripts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-scripts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;a href="https://gist.github.com/alanhe421/59c2def0e2491b2f74bf5887f5509285" target="_blank" rel="noopener">JD Review&lt;/a>&lt;/p>
&lt;p>Right-click - automatic commenting to earn a little Jingdou.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-24-125551.jpeg"
alt="https://static.1991421.cn/2023/2023-12-24-125551.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://gist.github.com/alanhe421/59eb136af12ed8df7cf3ffe1c9eec7fe" target="_blank" rel="noopener">CITIC Bank Repayment&lt;/a>&lt;/p>
&lt;p>After entering the China CITIC Bank page, automatically go to my loan page, statistics on current interest ratio and other information. The bank webpage doesn&amp;rsquo;t show this information, but these are the most important.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-24-130614.jpeg"
alt="https://static.1991421.cn/2023/2023-12-24-130614.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://gist.github.com/alanhe421/1b204366502dfef03c7e4b5185499a8a" target="_blank" rel="noopener">Video Speed Control&lt;/a>&lt;/p>
&lt;p>Some web pages don&amp;rsquo;t offer video playback at multiple speeds, so the script adds a right-click menu for speed switching.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-24-131240.jpeg"
alt="https://static.1991421.cn/2023/2023-12-24-131240.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://gist.github.com/alanhe421/dc41f81f4b8ece50e5141c20d30ea963" target="_blank" rel="noopener">Text Selection Support&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Work Customization Scripts&lt;/p>
&lt;ul>
&lt;li>For example, when loading a certain webpage, automatically perform some option operations.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="tampermonkey-setup">
&lt;a class="heading-anchor-link" href="#tampermonkey-setup">Tampermonkey setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tampermonkey-setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Enable &lt;code>Sync Service&lt;/code>&lt;/p>
&lt;p>If using multiple devices, it&amp;rsquo;s recommended to enable sync service.&lt;/p>
&lt;p>Note: First, set the configuration mode to Advanced. If the run button in Userscript is grayed out, click save first.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-24-151055.jpeg"
alt="https://static.1991421.cn/2023/2023-12-24-151055.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-24-151156.jpeg"
alt="https://static.1991421.cn/2023/2023-12-24-151156.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="formatting-code">
&lt;a class="heading-anchor-link" href="#formatting-code">Formatting code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="formatting-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The plugin itself doesn&amp;rsquo;t provide code formatting tools, it&amp;rsquo;s recommended to use VSCode or other editors.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you frequently use certain web pages but are unsatisfied, consider whether they can be enhanced with Tampermonkey.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.tampermonkey.net/documentation.php?locale=en" target="_blank" rel="noopener">Tampermonkey official documentation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>The inspect Command in Node</title><link>https://en.1991421.cn/2023/12/20/nodeinspect/</link><pubDate>Wed, 20 Dec 2023 23:53:19 +0800</pubDate><guid>https://en.1991421.cn/2023/12/20/nodeinspect/</guid><description>&lt;blockquote>
&lt;p>When using WebStorm/Visual Studio Code, for scripts defined in package.json, we can directly click debug to start the script. Why does this allow debugging? Here&amp;rsquo;s a coarse-grained explanation.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-12-21-000349.jpeg"
alt="https://static.1991421.cn/2023/2023-12-21-000349.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="debugging-in-ide">
&lt;a class="heading-anchor-link" href="#debugging-in-ide">Debugging in IDE&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="debugging-in-ide"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, the original script is &lt;code> &amp;quot;start&amp;quot;: &amp;quot;node app.js&amp;quot;,&lt;/code> When we need to debug this Node service, clicking the debug button shows the following print information:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/Users/alanhe/.nvm/versions/node/v18.16.1/bin/npm run start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; express-demo@0.0.1 start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; node app.js
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Debugger listening on ws://127.0.0.1:63206/3fc8664c-10d9-4f99-8ed8-04efd382d9ae
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">For help, see: https://nodejs.org/en/docs/inspector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Debugger attached.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Example app listening on port http://127.0.0.1:8000!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can see an additional ws request, which is the service debugging connection. This essentially means it&amp;rsquo;s the inspector under Node.js.&lt;/p>
&lt;p>What does the IDE do here? It actually adds the inspect parameter settings.&lt;/p>
&lt;h2 id="inspect-in-node">
&lt;a class="heading-anchor-link" href="#inspect-in-node">inspect in Node&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="inspect-in-node"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above debug is equivalent to the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">node --inspect=127.0.0.1:63206 app.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Therefore, even without using IDE debug, it&amp;rsquo;s possible to do this. However, when we directly use this command, it won&amp;rsquo;t hit IDE breakpoints yet. We still need to configure it in the IDE.&lt;/p>
&lt;p>For WebStorm, you need to start &lt;code>Attach to Node.js/Chrome&lt;/code>. For VSC, it&amp;rsquo;s &lt;code>Debug:Attach to Node Process&lt;/code>.&lt;/p>
&lt;p>After attaching, you can normally debug breakpoints in the IDE.&lt;/p>
&lt;ol>
&lt;li>Detailed explanation of inspect &lt;a href="https://nodejs.org/api/cli.html#cli_inspect_host_port" target="_blank" rel="noopener">click here&lt;/a>&lt;/li>
&lt;li>The default inspect configuration is &lt;code>127.0.0.1:9229&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The reason for being confused about this issue is that when inspect is manually configured in the script, and then debug operation is performed in the IDE, you&amp;rsquo;ll find that breakpoints can&amp;rsquo;t be hit. The reason is that with manual inspect, the IDE cannot automatically capture the corresponding process, so it doesn&amp;rsquo;t work.&lt;/p></description></item><item><title>Common npm Packages for Frontend Development</title><link>https://en.1991421.cn/2023/12/20/common-npm-packages/</link><pubDate>Wed, 20 Dec 2023 23:04:11 +0800</pubDate><guid>https://en.1991421.cn/2023/12/20/common-npm-packages/</guid><description>&lt;blockquote>
&lt;p>Here&amp;rsquo;s a list of npm packages commonly used by frontend developers&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>&lt;a href="https://www.npmjs.com/package/local-web-server" target="_blank" rel="noopener">local-web-server&lt;/a>&lt;/p>
&lt;p>Run &lt;code>ws&lt;/code> to start a web server instantly&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.npmjs.com/package/whistle" target="_blank" rel="noopener">whistle&lt;/a>&lt;/p>
&lt;p>Proxy service&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.npmjs.com/package/chalk" target="_blank" rel="noopener">chalk&lt;/a>&lt;/p>
&lt;p>Colorize console output&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.npmjs.com/package/readline" target="_blank" rel="noopener">readline&lt;/a>&lt;/p>
&lt;p>Terminal interactive input&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Ways to Open a New Page in JavaScript</title><link>https://en.1991421.cn/2023/12/20/js-open-new-page/</link><pubDate>Wed, 20 Dec 2023 22:54:05 +0800</pubDate><guid>https://en.1991421.cn/2023/12/20/js-open-new-page/</guid><description>&lt;blockquote>
&lt;p>Frontend code often needs to open new pages. Here is a summary of methods and issues encountered.&lt;/p>
&lt;/blockquote>
&lt;h2 id="methods">
&lt;a class="heading-anchor-link" href="#methods">Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Anchor tag&lt;/li>
&lt;li>window.open&lt;/li>
&lt;/ol>
&lt;p>window gives finer control, such as width and height. For anchor tags, you need to create a DOM element and decide whether it is visible. Anchor tags are generally preferred because they represent user-driven interactions.&lt;/p>
&lt;h2 id="behavior-in-pwa">
&lt;a class="heading-anchor-link" href="#behavior-in-pwa">Behavior in PWA&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="behavior-in-pwa"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A PWA is just a presentation mode for web pages. If you open a new page in a PWA, the behavior differs:&lt;/p>
&lt;ol>
&lt;li>Anchor tags open in the traditional browser UI.&lt;/li>
&lt;li>window.open follows the PWA display mode. For example, if the PWA uses standalone, the new window also uses standalone.&lt;/li>
&lt;/ol>
&lt;p>In my opinion, in PWAs you should use location.href rather than opening a new tab. Try to keep an app-like experience.&lt;/p></description></item><item><title>guacamole-common-js</title><link>https://en.1991421.cn/2023/12/10/guacamole-common-js/</link><pubDate>Sun, 10 Dec 2023 22:24:47 +0800</pubDate><guid>https://en.1991421.cn/2023/12/10/guacamole-common-js/</guid><description>&lt;blockquote>
&lt;p>Recently encountered an issue where after the remote desktop is started, switching to other tab pages causes input events to fail.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">keyboard&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Guacamole&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Keyboard&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">document&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">keyboard&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onkeydown&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">keysym&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Do something ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">keyboard&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onkeyup&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">keysym&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Do something ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The code is roughly as shown above.&lt;/p>
&lt;h2 id="cause">
&lt;a class="heading-anchor-link" href="#cause">Cause&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cause"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Guacamole.Keyboard captures events and prevents bubbling, so as long as it&amp;rsquo;s not destroyed, it will continue to capture all keyboard events on the webpage.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Save the keyboard.onkeydown/onkeyup bound events elsewhere, set keyboard.onkeydown=null, keyboard.onkeyup=null. Then restore them when needed.&lt;/li>
&lt;li>Set the binding element for new Guacamole.Keyboard to the RDP desktop element&lt;/li>
&lt;/ol>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://guacamole.apache.org/doc/gug/guacamole-common-js.html#keyboard" target="_blank" rel="noopener">https://guacamole.apache.org/doc/gug/guacamole-common-js.html#keyboard&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This issue demonstrates the importance of understanding how Guacamole&amp;rsquo;s keyboard event handling works. The solution involves either temporarily disabling the keyboard event handlers or binding them to a more specific element to prevent them from capturing events across the entire document when not needed.&lt;/p></description></item><item><title>Online Failure - Hidden Timer Issues</title><link>https://en.1991421.cn/2023/12/09/online-failure-hidden-timer/</link><pubDate>Sat, 09 Dec 2023 19:45:51 +0800</pubDate><guid>https://en.1991421.cn/2023/12/09/online-failure-hidden-timer/</guid><description>&lt;blockquote>
&lt;p>Recently encountered a serious issue online where our server was frantically sending heartbeat requests to another SDK service provider. After investigation, it turned out to be caused by an overlooked point related to timers, so marking it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="problem-situation">
&lt;a class="heading-anchor-link" href="#problem-situation">Problem Situation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-situation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The current situation is that one service IP made over &lt;code>4000&lt;/code> requests per minute. Actually, under normal circumstances, it should only report once every &lt;code>5 minutes&lt;/code>.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The entire troubleshooting process was relatively simple/clear&lt;/p>
&lt;ol>
&lt;li>After confirming, the heartbeat request is automatically triggered by the SDK itself when initializing instance objects. Our logic has no heartbeat setup or heartbeat calling logic, so it must be an initialization logic problem
&lt;ul>
&lt;li>The SDK class constructor has &lt;code>setInterval&lt;/code> logic&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Querying the program revealed there are currently 4 places initializing instance objects, with 1 place initializing at service startup, and the other 3 triggered during API calls. So if API calls are frequent enough, it would bring N timers&lt;/li>
&lt;li>Checking production logs, the increased frequency of related API calls also correlates with data time points monitored by another service&lt;/li>
&lt;/ol>
&lt;p>OK, after identifying the problem, the solution was also clear - remove the logic of instantiating SDK objects during API calls. According to our actual situation, we should maintain a singleton and initialize it at service startup.&lt;/p>
&lt;p>After fixing, re-monitoring showed it&amp;rsquo;s back to normal.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>When JS functions finish executing, they destroy related objects but do not destroy timers. Therefore, when calling logic that starts timers, you must pay attention to cleanup logic.&lt;/li>
&lt;li>As above, calling certain functions that contain hidden timer logic can be a pitfall. This needs extra attention.&lt;/li>
&lt;/ol></description></item><item><title>Implementing Input Autocomplete Visuals</title><link>https://en.1991421.cn/2023/11/25/input-autocomplete/</link><pubDate>Sat, 25 Nov 2023 17:28:50 +0800</pubDate><guid>https://en.1991421.cn/2023/11/25/input-autocomplete/</guid><description>&lt;blockquote>
&lt;p>While building a WebShell UI I needed an input box that shows autocomplete suggestions inline. The logic was straightforward, but the visual treatment required a small trick.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-11-25-173413.jpeg"
alt="https://static.1991421.cn/2023/2023-11-25-173413.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>In the screenshot, typing &lt;code>h&lt;/code> reveals &lt;code>ello&lt;/code> as the completion. The suffix must appear in a different color.&lt;/p>
&lt;p>Browsers don’t let you color arbitrary substrings inside an &lt;code>&amp;lt;input&amp;gt;&lt;/code> with pure CSS. That means we need a DOM + JavaScript workaround.&lt;/p>
&lt;h2 id="approach">
&lt;a class="heading-anchor-link" href="#approach">Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Insert a &lt;code>&amp;lt;div&amp;gt;&lt;/code> before the input that displays the full completion string. Give it the desired completion color.&lt;/li>
&lt;li>Absolutely position the div so its text overlaps the input text exactly.&lt;/li>
&lt;li>Keep the input on top using stacking context so the user can still place the caret and type normally.&lt;/li>
&lt;/ol>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>The higher z-index on the input prevents the completion layer from intercepting the cursor or mouse selection.&lt;/li>
&lt;li>The completion div should contain the full string (e.g., &lt;code>hello&lt;/code>). Match the font size, weight, letter-spacing, etc., so the overlay aligns perfectly with the input.&lt;/li>
&lt;/ul>
&lt;h2 id="demo">
&lt;a class="heading-anchor-link" href="#demo">Demo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="demo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://github.com/alanhe421/express-demo/blob/master/test/input-completion/index.html" target="_blank" rel="noopener">https://github.com/alanhe421/express-demo/blob/master/test/input-completion/index.html&lt;/a>&lt;/p></description></item><item><title>Copy/Paste Support in WebShell</title><link>https://en.1991421.cn/2023/11/23/webshell/</link><pubDate>Thu, 23 Nov 2023 23:27:19 +0800</pubDate><guid>https://en.1991421.cn/2023/11/23/webshell/</guid><description>&lt;blockquote>
&lt;p>I recently ran into issues with copy/paste support in WebShell. Here’s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="browser-clipboard-permissions">
&lt;a class="heading-anchor-link" href="#browser-clipboard-permissions">Browser Clipboard Permissions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-clipboard-permissions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When clipboard permissions are disabled, using the Clipboard API throws errors. Some users’ browsers restrict clipboard access for security. Can WebShell still support copy/paste? Yes—browsers also have default copy and paste hotkeys.&lt;/p>
&lt;h3 id="browser-copypaste">
&lt;a class="heading-anchor-link" href="#browser-copypaste">Browser Copy/Paste&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-copypaste"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>On Windows, copy is &lt;code>Ctrl+C&lt;/code>, paste is &lt;code>Ctrl+V&lt;/code>.&lt;/p>
&lt;p>On macOS, copy is &lt;code>Cmd+C&lt;/code>, paste is &lt;code>Cmd+V&lt;/code>.&lt;/p>
&lt;p>These hotkeys are fixed; pressing them triggers the corresponding default behavior.&lt;/p>
&lt;p>However, page logic can intercept and prevent the default behavior, which can cause issues—for example, with xterm.js on Windows.&lt;/p>
&lt;h2 id="ctrlcctrlv-in-xtermjs">
&lt;a class="heading-anchor-link" href="#ctrlcctrlv-in-xtermjs">Ctrl+C/Ctrl+V in xterm.js&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ctrlcctrlv-in-xtermjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>xterm.js listens for and handles key events, which can prevent these hotkeys from working. How to fix this?&lt;/p>
&lt;p>Use &lt;code>attachCustomKeyEventHandler&lt;/code> to handle hotkeys and return &lt;code>false&lt;/code> so the default behavior continues.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">customKeyEventHandler&lt;/span> &lt;span class="err">–&lt;/span> &lt;span class="nx">The&lt;/span> &lt;span class="nx">custom&lt;/span> &lt;span class="nx">KeyboardEvent&lt;/span> &lt;span class="nx">handler&lt;/span> &lt;span class="nx">to&lt;/span> &lt;span class="nx">attach&lt;/span>&lt;span class="p">.&lt;/span> &lt;span class="nx">This&lt;/span> &lt;span class="nx">is&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">that&lt;/span> &lt;span class="nx">takes&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="nx">KeyboardEvent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">allowing&lt;/span> &lt;span class="nx">consumers&lt;/span> &lt;span class="nx">to&lt;/span> &lt;span class="nx">stop&lt;/span> &lt;span class="nx">propagation&lt;/span> &lt;span class="nx">and&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">or&lt;/span> &lt;span class="nx">prevent&lt;/span> &lt;span class="nx">the&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span> &lt;span class="nx">The&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">returns&lt;/span> &lt;span class="nx">whether&lt;/span> &lt;span class="nx">the&lt;/span> &lt;span class="nx">event&lt;/span> &lt;span class="nx">should&lt;/span> &lt;span class="nx">be&lt;/span> &lt;span class="nx">processed&lt;/span> &lt;span class="nx">by&lt;/span> &lt;span class="nx">xterm&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">js&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">attachCustomKeyEventHandler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">customKeyEventHandler&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">KeyboardEvent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Browser hotkeys can’t fully replace the Clipboard API. When clipboard is unavailable, can we still implement copy/paste?&lt;/p>
&lt;p>The answer is: partially.&lt;/p>
&lt;h2 id="documentexeccommandcopy">
&lt;a class="heading-anchor-link" href="#documentexeccommandcopy">document.execCommand(&amp;lsquo;copy&amp;rsquo;)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="documentexeccommandcopy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The document’s &lt;code>execCommand&lt;/code> method supports copy. In testing, even without Clipboard API, you can dynamically copy specific text to the clipboard.&lt;/p>
&lt;p>Paste isn’t supported (IE reportedly supports it). Note that this API is deprecated and considered legacy. Still, as a fallback for copy, it’s a reasonable option.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s pretty much it for copy/paste.&lt;/p>
&lt;p>In summary, when Clipboard API access is unavailable, you can still rely on the browser’s default hotkeys for basic text copy/paste—just make sure you don’t block the default behavior.&lt;/p>
&lt;p>done.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/xtermjs/xterm.js/issues/2478" target="_blank" rel="noopener">https://github.com/xtermjs/xterm.js/issues/2478&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use Mousetrap (Step-by-Step Guide)</title><link>https://en.1991421.cn/2023/11/19/8a717ea/</link><pubDate>Sun, 19 Nov 2023 11:42:09 +0800</pubDate><guid>https://en.1991421.cn/2023/11/19/8a717ea/</guid><description>&lt;blockquote>
&lt;p>For web hotkey listening and handling, you can use mousetrap.js. This library is no longer maintained, but it&amp;rsquo;s still very useful and recommended.&lt;/p>
&lt;p>Here&amp;rsquo;s an introduction to its usage.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-11-19-151351.jpg"
alt="https://static.1991421.cn/2023/2023-11-19-151351.jpg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="features">
&lt;a class="heading-anchor-link" href="#features">Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Supports single/combined keys/Gmail-style/Konami Code key bindings&lt;/p>
&lt;ol>
&lt;li>Gmail-style means like &lt;code>g i&lt;/code>, where consecutive key inputs can execute an action. The advantage of this hotkey style is that it easily avoids browser/plugin hotkeys&lt;/li>
&lt;li>Konami Code is somewhat like in Contra where specific key combinations trigger 30 lives. Personally, I think this type of key binding is quite suitable for web easter eggs if necessary&lt;/li>
&lt;/ol>
&lt;p>In addition to basic hotkey binding features, Mousetrap also has some extensions to solve specific needs&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Pause/unpause&lt;/p>
&lt;p>Stop and resume this Mousetrap&amp;rsquo;s bindings&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Record&lt;/p>
&lt;p>Hotkey recording, useful when implementing custom web hotkeys&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Bind dictionary&lt;/p>
&lt;p>Supports dictionary format for binding multiple hotkeys at once.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>The Mousetrap constructor parameter is the element to bind hotkey listening to. If not provided, it defaults to document&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">mousetrap&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Mousetrap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>bind&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;command+k&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;g i&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">combokey&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">highlight&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">combokey&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: The second parameter of the callback is the key combination, such as &lt;code>command+k&lt;/code> or &lt;code>g i&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>unbind&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unbind&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;command+k&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: The keys for unbind are the same as for bind, so they can also be arrays/strings&lt;/p>
&lt;/li>
&lt;li>
&lt;p>trigger&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trigger&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;g i&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>stopCallback&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stopCallback&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Controls callback prevention logic. By default, hotkey bindings are not triggered in input/textarea/editable situations. As shown above, it triggers in all request situations&lt;/p>
&lt;/li>
&lt;li>
&lt;p>reset()&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reset&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Reset means delete all bindings&lt;/p>
&lt;/li>
&lt;li>
&lt;p>addKeycodes&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addKeycodes&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">144&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;numlock&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Add keycode mappings, usually don&amp;rsquo;t need to change. This is because the library is aging, and the entire implementation uses keycode instead of key. Currently MDN no longer recommends using KeyCode&lt;/p>
&lt;/li>
&lt;li>
&lt;p>record&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">mousetrap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sequence&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;You pressed: &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">sequence&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: The record timeout is fixed at 1s and does not support continuous recording&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="mousetrap-issues">
&lt;a class="heading-anchor-link" href="#mousetrap-issues">Mousetrap Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mousetrap-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>The record timeout is hardcoded to 1s. Currently, you need to modify the source code to extend it as a parameter, or change the value to solve this&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">_restartRecordTimer&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">clearTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">_recordTimer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_recordTimer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">_finishRecording&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Mousetrap uses event bubbling. If you want to use event capturing, you also need to modify the source code to extend support&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">_addEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">object&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">callback&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">callback&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">attachEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;on&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">callback&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>The record plugin import and packaging will encounter errors because i is undefined in the for loop, which causes errors in strict mode packaging.&lt;/p>
&lt;pre>&lt;code> ```js
for (i = 0; i &amp;lt; modifiers.length; ++i) {
_recordKey(modifiers[i]);
}
```
&lt;/code>&lt;/pre>
&lt;/li>
&lt;/ol>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://craig.is/killing/mice" target="_blank" rel="noopener">https://craig.is/killing/mice&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/ccampbell/mousetrap" target="_blank" rel="noopener">https://github.com/ccampbell/mousetrap&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you need to implement hotkey binding in WEB, you can still consider using Mousetrap&lt;/p></description></item><item><title>Command Palette in WebShell</title><link>https://en.1991421.cn/2023/11/18/webshell/</link><pubDate>Sat, 18 Nov 2023 20:02:35 +0800</pubDate><guid>https://en.1991421.cn/2023/11/18/webshell/</guid><description>&lt;blockquote>
&lt;p>The WebShell command palette feature recently launched. Here are the design notes and considerations.&lt;/p>
&lt;/blockquote>
&lt;h2 id="problem-statement">
&lt;a class="heading-anchor-link" href="#problem-statement">Problem Statement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-statement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Why build this?&lt;/p>
&lt;p>As WebShell’s features grow, user “action paths” get longer. Example: logging into a machine without a command palette might require:&lt;/p>
&lt;ol>
&lt;li>Click Connections&lt;/li>
&lt;li>Focus the search input&lt;/li>
&lt;li>Select a connection config&lt;/li>
&lt;li>Click Login&lt;/li>
&lt;/ol>
&lt;p>This path is long. The command palette offers an interaction similar to Alfred/Raycast on macOS: keep your hands on the keyboard to execute actions quickly.&lt;/p>
&lt;p>With a command palette:&lt;/p>
&lt;ol>
&lt;li>Use a hotkey to open the palette&lt;/li>
&lt;li>Use the input + arrow keys to choose a connection&lt;/li>
&lt;li>Press Enter to log in&lt;/li>
&lt;/ol>
&lt;p>The number of steps may be similar, but you never leave the keyboard, improving flow.&lt;/p>
&lt;h2 id="design">
&lt;a class="heading-anchor-link" href="#design">Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>At its core, the palette displays a list of actions. Let’s define a &lt;code>CommandAction&lt;/code>. Each menu item is a CommandAction.&lt;/p>
&lt;h2 id="commandaction">
&lt;a class="heading-anchor-link" href="#commandaction">CommandAction&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="commandaction"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">uid&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon?&lt;/span>: &lt;span class="kt">React.ReactNode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">match?&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">item?&lt;/span>: &lt;span class="kt">CommandAction&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ReactNode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hotkey?&lt;/span>: &lt;span class="kt">GlobalAction&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">action&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">setQuery&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">setQuery&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">q&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">actionMap&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">comboKey&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">close&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shaking&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">windowDontCloseOnAction?&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">preCheck&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ReactNode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">variables&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">any&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Some design notes:&lt;/p>
&lt;ol>
&lt;li>&lt;code>uid&lt;/code> uniquely identifies a command for things like history ordering.&lt;/li>
&lt;li>&lt;code>match&lt;/code> follows Alfred’s idea: matching only on &lt;code>title&lt;/code> can be limiting. For example, a “search” keyword might match Google. With &lt;code>match&lt;/code>, you can add extra keywords to improve recall quality.&lt;/li>
&lt;li>&lt;code>action&lt;/code> defines the Enter behavior. As the palette grows, Enter alone may not be enough. Add modifier+Enter combos via &lt;code>actionMap&lt;/code> to expand functionality. Think of &lt;code>action&lt;/code> as the &lt;code>enter&lt;/code> entry in &lt;code>actionMap&lt;/code>.&lt;/li>
&lt;li>&lt;code>hotkey&lt;/code> shows the command’s shortcut.&lt;/li>
&lt;li>&lt;code>variables&lt;/code> holds contextual variables when the command is highlighted/executed.&lt;/li>
&lt;/ol>
&lt;h2 id="commandaction-list">
&lt;a class="heading-anchor-link" href="#commandaction-list">CommandAction List&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="commandaction-list"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Where does the list come from? Some are static (declared in code), others are generated dynamically via JS, e.g., fetched from the backend. The palette renders whatever list it receives.&lt;/p>
&lt;h3 id="searchfilter">
&lt;a class="heading-anchor-link" href="#searchfilter">Search/Filter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="searchfilter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For the full list, filtering is straightforward:&lt;/p>
&lt;ol>
&lt;li>Fuzzy-match against &lt;code>title&lt;/code>. If the UI is Chinese, supporting pinyin search improves UX.
&lt;ul>
&lt;li>Keep the full list in memory and filter on the query.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>For &lt;code>match&lt;/code>, use exact matches. If it were fuzzy too, recall might be too broad and reduce quality.&lt;/li>
&lt;/ol>
&lt;h3 id="multi-level-commandaction-lists">
&lt;a class="heading-anchor-link" href="#multi-level-commandaction-lists">Multi-level CommandAction Lists&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="multi-level-commandaction-lists"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>As the palette grows, one level isn’t enough. For example, you might want to pick a specific SSH profile within the palette and press Enter to connect. Hence, multi-level support.&lt;/p>
&lt;p>How to link levels?&lt;/p>
&lt;h4 id="method-1">Method 1&lt;/h4>&lt;p>Maintain a &lt;code>ScriptCommandsMap&lt;/code>: key = trigger keyword, value = a function that generates the list. The trigger keyword is not the user’s filter text; it’s the activation keyword. The top level could be empty; a second-level list might use a keyword like &lt;code>connect&lt;/code>. When the user types &lt;code>connect&lt;/code>, the palette resolves the function and renders the generated list.&lt;/p>
&lt;h4 id="method-2">Method 2&lt;/h4>&lt;p>Add a &lt;code>type&lt;/code> to CommandAction. If it’s a subcommand, pressing Enter shouldn’t close the palette; instead, the &lt;code>action&lt;/code> returns a list to display next.&lt;/p>
&lt;p>I currently use Method 1, but may switch to Method 2 to avoid a large central map and let each command handle its own logic, improving maintainability.&lt;/p>
&lt;h2 id="related-products">
&lt;a class="heading-anchor-link" href="#related-products">Related Products&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-products"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Some terminal products offer similar features:&lt;/p>
&lt;ul>
&lt;li>xterminal: single-level only&lt;/li>
&lt;li>warp: supports multi-level (e.g., sessions listing)&lt;/li>
&lt;/ul></description></item><item><title>How to Use the keyboard.lock API (Step-by-Step Guide)</title><link>https://en.1991421.cn/2023/11/12/keyboard-lock/</link><pubDate>Sun, 12 Nov 2023 15:30:13 +0800</pubDate><guid>https://en.1991421.cn/2023/11/12/keyboard-lock/</guid><description>&lt;blockquote>
&lt;p>I have been working on custom hotkeys for WebShell. The issue is that web hotkeys are limited by the browser and operating system, so some hotkeys cannot be captured, such as &lt;code>command t&lt;/code>.&lt;/p>
&lt;p>After some research, I found that &lt;code>navigator.keyboard.lock&lt;/code> can solve this limitation. Here are some notes and takeaways from using it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="hotkeys-that-cannot-be-intercepted">
&lt;a class="heading-anchor-link" href="#hotkeys-that-cannot-be-intercepted">Hotkeys That Cannot Be Intercepted&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hotkeys-that-cannot-be-intercepted"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Taking macOS as an example, the following hotkeys cannot be intercepted in real tests. That is, at the JS layer we cannot capture these keys and therefore cannot prevent the default behavior.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">⌘ N Create new window
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⌘ T Create new Tab
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⌘ W Close Tab
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⌘ ⇧ [ Previous Tab
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">⌘ ⇧ ] Next Tab
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This seems unsolvable, but with the lock API, these may become possible to handle.&lt;/p>
&lt;h2 id="lock-api">
&lt;a class="heading-anchor-link" href="#lock-api">Lock API&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lock-api"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;code>lock&lt;/code> can be called without parameters to lock all keys.&lt;/li>
&lt;li>&lt;code>lock&lt;/code> can take parameters to lock specific keys, and it does not support locking modifier keys.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">keyboard&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lock&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;KeyW&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;KeyA&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;KeyS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;KeyD&amp;#34;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>&lt;code>Currently only supported in fullscreen mode&lt;/code>. In non-fullscreen mode, &lt;code>lock&lt;/code> does not throw errors but also does not work.&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;ul>
&lt;li>System keyboard lock is only available when the document is in &lt;a href="https://fullscreen.spec.whatwg.org/#fullscreen-enabled-flag" target="_blank" rel="noopener">fullscreen mode&lt;/a>, and only from a secure origin.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>See &lt;a href="https://github.com/WICG/keyboard-lock/blob/gh-pages/explainer.md" target="_blank" rel="noopener">https://github.com/WICG/keyboard-lock/blob/gh-pages/explainer.md&lt;/a>&lt;/p>
&lt;ol start="4">
&lt;li>Compatibility: &lt;code>Chrome/Edge/Opera are supported, Firefox/Safari are not&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>See &lt;a href="https://caniuse.com/mdn-api_keyboard_lock" target="_blank" rel="noopener">https://caniuse.com/mdn-api_keyboard_lock&lt;/a>&lt;/p>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://keyboard-lock.glitch.me/" target="_blank" rel="noopener">https://keyboard-lock.glitch.me/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/alanhe421/express-demo/blob/master/test/keyboard-lock/lock.html" target="_blank" rel="noopener">https://github.com/alanhe421/express-demo/blob/master/test/keyboard-lock/lock.html&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="in-pwa">
&lt;a class="heading-anchor-link" href="#in-pwa">In PWA&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="in-pwa"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In PWA, even without fullscreen, &lt;code>navigator.keyboard.lock&lt;/code> can still capture keys. But the behavior is not consistent across OSes. So even for PWA, it is recommended to use hotkeys in fullscreen for the best experience.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The lock API addresses the pain point of web hotkeys being restricted. Unfortunately, several mainstream browsers still do not fully support it. Looking forward to the day it is widely supported.&lt;/p></description></item><item><title>Displaying Images in WebShell</title><link>https://en.1991421.cn/2023/11/05/97229483/</link><pubDate>Sun, 05 Nov 2023 23:11:51 +0800</pubDate><guid>https://en.1991421.cn/2023/11/05/97229483/</guid><description>&lt;blockquote>
&lt;p>Can a terminal display images? Yes. Here’s a summary of how to display images in xterm.js.&lt;/p>
&lt;/blockquote>
&lt;h2 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-11-05-230539.jpeg"
alt="https://static.1991421.cn/2023/2023-11-05-230539.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-11-05-230627.jpeg"
alt="https://static.1991421.cn/2023/2023-11-05-230627.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="implementation">
&lt;a class="heading-anchor-link" href="#implementation">Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="server-side">
&lt;a class="heading-anchor-link" href="#server-side">Server side&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="server-side"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Using a Mac as the target server for installing libraries:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># sixel protocol&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install libsixel
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># imgcat can also be installed via iTerm2’s Shell integration package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install eddieantonio/eddieantonio/imgcat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="webshell">
&lt;a class="heading-anchor-link" href="#webshell">WebShell&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webshell"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Frontend setup is straightforward: load &lt;code>@xterm/addon-image&lt;/code>.&lt;/p>
&lt;p>Note: &lt;code>@xterm/addon-image&lt;/code> is an official xterm addon.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">npm&lt;/span> &lt;span class="nx">install&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="nx">save&lt;/span> &lt;span class="err">@&lt;/span>&lt;span class="nx">xterm&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">addon&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">image&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">customSettings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">enableSizeReports&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// whether to enable CSI t reports (see below)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">pixelLimit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">16777216&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// max. pixel size of a single image
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">sixelSupport&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// enable sixel support
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">sixelScrolling&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// whether to scroll on image output
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">sixelPaletteLimit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">256&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// initial sixel palette size
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">sixelSizeLimit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">25000000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// size limit of a single sixel sequence
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">storageLimit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">128&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// FIFO storage limit in MB
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">showPlaceholder&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// whether to show a placeholder for evicted images
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">iipSupport&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// enable iTerm IIP support
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">iipSizeLimit&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">20000000&lt;/span> &lt;span class="c1">// size limit of a single IIP sequence
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">imageAddon&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">ImageAddon&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ImageAddon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">customSettings&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">loadAddon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">imageAddon&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="when-the-image-addon-isnt-loaded">
&lt;a class="heading-anchor-link" href="#when-the-image-addon-isnt-loaded">When the image addon isn’t loaded&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-the-image-addon-isnt-loaded"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If WebShell doesn’t load the ImageAddon, the output looks like this. The image data is wrapped in escape sequences, so it won’t render in the terminal by default.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-11-05-230505.jpeg"
alt="https://static.1991421.cn/2023/2023-11-05-230505.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With this, WebShell can display images in the terminal. As for the actual value of showing images in a terminal—that depends on your design and use cases.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/saitoha/libsixel/tree/master" target="_blank" rel="noopener">https://github.com/saitoha/libsixel/tree/master&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/eddieantonio/imgcat" target="_blank" rel="noopener">https://github.com/eddieantonio/imgcat&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/xtermjs/xterm.js/tree/master/addons/addon-image" target="_blank" rel="noopener">https://github.com/xtermjs/xterm.js/tree/master/addons/addon-image&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use CSS :has() Function (Step-by-Step Guide)</title><link>https://en.1991421.cn/2023/11/01/css-has/</link><pubDate>Wed, 01 Nov 2023 10:13:08 +0800</pubDate><guid>https://en.1991421.cn/2023/11/01/css-has/</guid><description>&lt;blockquote>
&lt;p>Recently while developing WebShell, I encountered a requirement to dynamically adjust styles - when the terminal enables split-screen mode with editor appearing on the top, the terminal at the bottom needs style adjustments, specifically adjusting the minimum height. Considering implementing this with CSS.&lt;/p>
&lt;/blockquote>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The :has() function can use CSS selectors to determine whether a certain element exists after or within child descendants. Therefore, using :has() can capture the editor element in split-screen situations, then normally use + to get sibling elements, and actually adjust styles. Of course, JS can also implement this, but CSS implementation is simpler.&lt;/p>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">terminal&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">min-height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#4bd58d&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">editor&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#003dfd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">editor&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">has&lt;/span>&lt;span class="o">(+&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">terminal&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">terminal&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ff0000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="compatibility">
&lt;a class="heading-anchor-link" href="#compatibility">Compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When using CSS functions, compatibility should always be considered. The :has() function is currently not supported in Firefox. However, since this functionality is just an enhancement here, compatibility doesn&amp;rsquo;t need to be considered. If compatibility is needed, consider implementing with JS.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>CSS functions can to some extent avoid implementation through JS, so they can be utilized more in development. Of course, compatibility also needs to be considered, as compatibility is a major challenge in frontend development.&lt;/p></description></item><item><title>Installing and Switching Shells</title><link>https://en.1991421.cn/2023/10/31/shell/</link><pubDate>Tue, 31 Oct 2023 10:27:11 +0800</pubDate><guid>https://en.1991421.cn/2023/10/31/shell/</guid><description>&lt;blockquote>
&lt;p>While adding shell hooks to a web shell project, I hit a few quirks. Here’s a cheat sheet for installing various shells and switching between them, using Ubuntu as the example OS.&lt;/p>
&lt;/blockquote>
&lt;h2 id="bash">
&lt;a class="heading-anchor-link" href="#bash">Bash&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">chsh -s &lt;span class="k">$(&lt;/span>which bash&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bash --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="zsh">
&lt;a class="heading-anchor-link" href="#zsh">Zsh&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="zsh"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo apt install zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chsh -s &lt;span class="k">$(&lt;/span>which zsh&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zsh --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="fish">
&lt;a class="heading-anchor-link" href="#fish">Fish&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fish"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo apt install fish
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo chsh -s &lt;span class="k">$(&lt;/span>which fish&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="tcsh">
&lt;a class="heading-anchor-link" href="#tcsh">Tcsh&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tcsh"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo apt install tcsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chsh -s &lt;span class="k">$(&lt;/span>which tcsh&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="notable-differences">
&lt;a class="heading-anchor-link" href="#notable-differences">Notable Differences&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notable-differences"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>In Bash and Zsh, &lt;code>printf &amp;quot;PreExecMarker;$1&amp;quot;;&lt;/code> doesn’t escape arguments, so placeholders like &lt;code>date +%Y&lt;/code> can trigger errors. Fish and Tcsh automatically escape arguments and avoid the issue.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-10-31-195452.jpeg"
alt="https://static.1991421.cn/2023/2023-10-31-195452.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Fix it by specifying the format explicitly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">printf&lt;/span> &lt;span class="s2">&amp;#34;PreExecMarker;%s&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;date +%Y&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-10-31-195625.jpeg"
alt="https://static.1991421.cn/2023/2023-10-31-195625.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Implementing SFTP File Management in WebShell</title><link>https://en.1991421.cn/2023/09/24/webshellsftp/</link><pubDate>Sun, 24 Sep 2023 16:44:40 +0800</pubDate><guid>https://en.1991421.cn/2023/09/24/webshellsftp/</guid><description>&lt;blockquote>
&lt;p>After enabling web-based terminal access via WebShell, users also need visual file management: upload/download, permission changes, inline editing, etc. Here’s a summary of the implementation.&lt;/p>
&lt;/blockquote>
&lt;h2 id="ssh2--ssh2-sftp-client">
&lt;a class="heading-anchor-link" href="#ssh2--ssh2-sftp-client">ssh2 + ssh2-sftp-client&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ssh2--ssh2-sftp-client"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After solving the terminal connection with &lt;code>ssh2&lt;/code>, use &lt;code>ssh2-sftp-client&lt;/code> to perform file operations:&lt;/p>
&lt;ol>
&lt;li>&lt;code>list&lt;/code> to fetch file lists
&lt;ul>
&lt;li>Returns file type, name, owner uid, group gid, size, atime, mtime&lt;/li>
&lt;li>For symlinks, &lt;code>type&lt;/code> is &lt;code>l&lt;/code>; hard links appear as &lt;code>-&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>get&lt;/code>/&lt;code>put&lt;/code> for streaming upload/download&lt;/li>
&lt;li>&lt;code>rename&lt;/code> to rename files&lt;/li>
&lt;li>&lt;code>delete&lt;/code> to delete files&lt;/li>
&lt;li>&lt;code>stat&lt;/code> to get file info&lt;/li>
&lt;li>&lt;code>mkdir&lt;/code> to create directories&lt;/li>
&lt;li>&lt;code>rename&lt;/code> to rename files&lt;/li>
&lt;/ol>
&lt;p>These cover the basics, but there are additional concerns.&lt;/p>
&lt;h2 id="issues">
&lt;a class="heading-anchor-link" href="#issues">Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Check whether the current user has permissions&lt;/li>
&lt;/ol>
&lt;p>First determine the current user’s &lt;code>uid&lt;/code>/&lt;code>gid&lt;/code>. Use &lt;code>ssh2.exec&lt;/code> to run &lt;code>id&lt;/code> to obtain them. Then compare with the file’s &lt;code>uid&lt;/code>/&lt;code>gid&lt;/code> from &lt;code>list&lt;/code>/&lt;code>stat&lt;/code> to infer permissions.&lt;/p>
&lt;ol start="2">
&lt;li>User list&lt;/li>
&lt;/ol>
&lt;p>The GUI needs to change file ownership. Since &lt;code>list&lt;/code>/&lt;code>stat&lt;/code> provides &lt;code>uid&lt;/code>/&lt;code>gid&lt;/code>, you only need a user list. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Output format:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># root,0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">grep /bin/bash /etc/passwd &lt;span class="p">|&lt;/span> awk -F: &lt;span class="s1">&amp;#39;{print $1 &amp;#34;,&amp;#34; $3}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>
&lt;p>Hidden files in lists&lt;/p>
&lt;p>To hide dotfiles, pass a filter (regex in older versions) when listing:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sr">/^[^.]/&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: SFTP doesn’t support server-side filtering; &lt;code>ssh2&lt;/code> still fetches all entries and filters client-side. In ssh2-sftp-client v9, &lt;code>list&lt;/code> switched from regex to a callback filter, which still works for this.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use the terminal session’s current working directory as the default open path&lt;/p>
&lt;p>Strictly speaking this is terminal-side, not SFTP. But it greatly improves UX. Implement via terminal hooks: after each command, run &lt;code>pwd&lt;/code> and send the result to the client. The client parses and caches the path; when opening SFTP, pass it as the initial &lt;code>path&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> /usr/local/bash-precmd/bash-preexec.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">preexec&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="nb">printf&lt;/span> &lt;span class="s2">&amp;#34;\x1B]1337;PreExec;Timestamp=&lt;/span>&lt;span class="k">$(&lt;/span>date +%s&lt;span class="k">)&lt;/span>&lt;span class="s2">;\x7&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">precmd&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="nb">printf&lt;/span> &lt;span class="s2">&amp;#34;\x1B]1337;PostExec;Exit=&lt;/span>&lt;span class="nv">$?&lt;/span>&lt;span class="s2">;CurrentDir=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">;Timestamp=&lt;/span>&lt;span class="k">$(&lt;/span>date +%s&lt;span class="k">)&lt;/span>&lt;span class="s2">;CurrentUser=&lt;/span>&lt;span class="nv">$USER&lt;/span>&lt;span class="s2">;\x7&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Beyond SFTP, there are other cases such as kubectl.&lt;/p>
&lt;h2 id="kubectl">
&lt;a class="heading-anchor-link" href="#kubectl">kubectl&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="kubectl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If managing containers via kubectl, SFTP won’t work. You’ll need to execute commands and parse the results.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Use &lt;code>ls&lt;/code> and friends to list files. Note that commands differ across container OSes and may need branching.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ls -l --time-style&lt;span class="o">=&lt;/span>full-iso /
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Add &lt;code>-A&lt;/code> to hide dotfiles&lt;/li>
&lt;li>&lt;code>--time-style=full-iso&lt;/code> stabilizes time format regardless of system locale&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Avoid &lt;code>stat&lt;/code> for file properties since output can be localized (e.g., “directory” differs by locale). Prefer &lt;code>ls&lt;/code> with &lt;code>-d&lt;/code> so the result is a single entry for the target file/dir. Also note that unlike &lt;code>ls -l&lt;/code>, there’s no first-line summary.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ls -ld --time-style&lt;span class="o">=&lt;/span>full-iso /
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>The above outlines the main path; real-world implementations are more complex, but the key points are here.&lt;/li>
&lt;li>After WebShell shipped file management, long-term observations showed it became the No.1 used feature—clearly very important.&lt;/li>
&lt;/ul></description></item><item><title>Integrating AI into WebShell</title><link>https://en.1991421.cn/2023/09/16/webshellai/</link><pubDate>Sat, 16 Sep 2023 23:43:22 +0800</pubDate><guid>https://en.1991421.cn/2023/09/16/webshellai/</guid><description>&lt;blockquote>
&lt;p>AI is empowering many products. WebShell now integrates AI services as well.&lt;/p>
&lt;p>Here is what I&amp;rsquo;ve built with AI so far and some TODOs.&lt;/p>
&lt;/blockquote>
&lt;img src="https://static.1991421.cn/2023/2023-11-01-234604.jpeg" alt="https://static.1991421.cn/2023/2023-11-01-234604.jpeg" style="zoom: 50%;" />
&lt;h2 id="prompt-engineering">
&lt;a class="heading-anchor-link" href="#prompt-engineering">Prompt engineering&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prompt-engineering"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>If the AI service supports a system prompt, you can set it like this:&lt;/p>
&lt;blockquote>
&lt;p>You are a Shell expert. I want to know how to complete tasks in the terminal or command line. I will ask shell-related questions and you return solutions. If asked who you are, reply with &lt;code>I am the WebShell AI assistant&lt;/code>. If asked non-shell questions, reply with &lt;code>Not shell-related, I don't know&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;/li>
&lt;li>
&lt;p>If the AI service doesn&amp;rsquo;t support system prompts, craft a user prompt instead:&lt;/p>
&lt;blockquote>
&lt;p>You are a Shell expert. I want to know how to complete tasks in the terminal or command line. I will ask shell-related questions and you return solutions. If asked who you are, reply with &lt;code>I am the WebShell AI assistant&lt;/code>. If asked non-shell questions, reply with &lt;code>Not shell-related, I don't know&lt;/code>.
My question is: {{userPrompt}}&lt;/p>
&lt;/blockquote>
&lt;p>User prompt engineering can play a role similar to a system prompt.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Prompt language matters&lt;/p>
&lt;p>Due to how LLMs work, the same prompt can yield different results depending on parameters like temperature. Language (Chinese vs English) also affects results. For domestic AI services, I use Chinese prompts.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="conversation-context-handling">
&lt;a class="heading-anchor-link" href="#conversation-context-handling">Conversation context handling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conversation-context-handling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Don&amp;rsquo;t send unlimited history. For example, keep the last 5 messages (2 turns + the latest user prompt). This ensures an odd number of messages.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>A &amp;ldquo;turn&amp;rdquo; is one user + one assistant message. If a user asked something and then stopped the response before the assistant replied, don&amp;rsquo;t include that orphaned user message in the next context - keep 1:1 pairing.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="token-costs">
&lt;a class="heading-anchor-link" href="#token-costs">Token costs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="token-costs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>AI usage is typically billed by tokens; both requests and responses consume tokens.&lt;/li>
&lt;li>To reduce costs, some APIs (e.g., OpenAI&amp;rsquo;s) support max token limits via &lt;code>max_tokens&lt;/code>. Some domestic APIs like Hunyuan/Wenxin don&amp;rsquo;t yet.&lt;/li>
&lt;li>Streaming vs non-streaming: in streaming mode, canceling the stream can interrupt generation on some providers, reducing tokens. Not all providers stop generation promptly.&lt;/li>
&lt;/ol>
&lt;h3 id="streaming">
&lt;a class="heading-anchor-link" href="#streaming">Streaming&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="streaming"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Benefits:&lt;/p>
&lt;ol>
&lt;li>Better UX: the model starts returning tokens as soon as they&amp;rsquo;re generated, so users see partial content sooner. Total completion time is similar to non-streaming.&lt;/li>
&lt;li>Potentially lower token usage if generation stops quickly after canceling (provider-dependent).&lt;/li>
&lt;/ol>
&lt;h2 id="todo">
&lt;a class="heading-anchor-link" href="#todo">TODO&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="todo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="fine-tuning">
&lt;a class="heading-anchor-link" href="#fine-tuning">Fine-tuning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fine-tuning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Prompt engineering can shape answers but is often insufficient. Fine-tuning helps steer answers for a class of queries by shifting output probabilities based on many examples.&lt;/p>
&lt;p>I haven&amp;rsquo;t done fine-tuning for WebShell yet - leaving this as a placeholder.&lt;/p>
&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://community.openai.com/t/chatgpts-stop-generating-function-how-to-implement/235121/7" target="_blank" rel="noopener">https://community.openai.com/t/chatgpts-stop-generating-function-how-to-implement/235121/7&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The AI service I use doesn&amp;rsquo;t support system prompts yet, so I&amp;rsquo;m composing user prompts, with imperfect results.&lt;/p></description></item><item><title>Developing Mini Programs with Taro</title><link>https://en.1991421.cn/2023/09/09/using-taro-to-develop-mini-program/</link><pubDate>Sat, 09 Sep 2023 00:07:44 +0800</pubDate><guid>https://en.1991421.cn/2023/09/09/using-taro-to-develop-mini-program/</guid><description>&lt;h2 id="introduction">
&lt;a class="heading-anchor-link" href="#introduction">Introduction&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="introduction"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Taro is a cross-platform development framework that allows you to write code once and compile it to run on multiple platforms including WeChat Mini Programs, Alipay Mini Programs, Baidu Smart Programs, ByteDance Mini Programs, QQ Mini Programs, and H5, React Native, and other platforms.&lt;/p>
&lt;h2 id="why-choose-taro">
&lt;a class="heading-anchor-link" href="#why-choose-taro">Why Choose Taro&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-choose-taro"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="advantages">
&lt;a class="heading-anchor-link" href="#advantages">Advantages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Write Once, Run Everywhere&lt;/strong>: Single codebase for multiple platforms&lt;/li>
&lt;li>&lt;strong>React-like Syntax&lt;/strong>: Familiar development experience for React developers&lt;/li>
&lt;li>&lt;strong>Component-based Development&lt;/strong>: Reusable components across platforms&lt;/li>
&lt;li>&lt;strong>Rich Ecosystem&lt;/strong>: Comprehensive toolchain and plugin system&lt;/li>
&lt;li>&lt;strong>TypeScript Support&lt;/strong>: Built-in TypeScript support&lt;/li>
&lt;li>&lt;strong>Performance Optimization&lt;/strong>: Optimized compilation and runtime&lt;/li>
&lt;/ol>
&lt;h3 id="supported-platforms">
&lt;a class="heading-anchor-link" href="#supported-platforms">Supported Platforms&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supported-platforms"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Mini Programs&lt;/strong>: WeChat, Alipay, Baidu, ByteDance, QQ, Kuaishou&lt;/li>
&lt;li>&lt;strong>Mobile Apps&lt;/strong>: React Native (iOS/Android)&lt;/li>
&lt;li>&lt;strong>Web&lt;/strong>: H5, Progressive Web Apps&lt;/li>
&lt;li>&lt;strong>Desktop&lt;/strong>: Electron (experimental)&lt;/li>
&lt;/ul>
&lt;h2 id="getting-started">
&lt;a class="heading-anchor-link" href="#getting-started">Getting Started&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-started"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install Taro CLI globally&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -g @tarojs/cli
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create new project&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">taro init myApp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Choose template&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ❯ TypeScript&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># JavaScript&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Vue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Vue3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="project-structure">
&lt;a class="heading-anchor-link" href="#project-structure">Project Structure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-structure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">myApp/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── pages/ # Page components
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └── index/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ├── index.tsx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ ├── index.config.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │ └── index.scss
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── components/ # Reusable components
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── utils/ # Utility functions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── services/ # API services
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── app.tsx # App entry
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── app.config.ts # App configuration
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── app.scss # Global styles
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── config/ # Build configuration
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── package.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── project.config.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="basic-configuration">
&lt;a class="heading-anchor-link" href="#basic-configuration">Basic Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="basic-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// app.config.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">defineAppConfig&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;pages/index/index&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;pages/profile/profile&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">backgroundTextStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">navigationBarBackgroundColor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#fff&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">navigationBarTitleText&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;My Taro App&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">navigationBarTextStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;black&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tabBar&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#666&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selectedColor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#b4282d&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">backgroundColor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#fafafa&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">borderStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;black&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">list&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pagePath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;pages/index/index&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">text&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Home&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">iconPath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;assets/home.png&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selectedIconPath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;assets/home-active.png&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pagePath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;pages/profile/profile&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">text&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Profile&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">iconPath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;assets/profile.png&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selectedIconPath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;assets/profile-active.png&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="component-development">
&lt;a class="heading-anchor-link" href="#component-development">Component Development&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-development"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="functional-components">
&lt;a class="heading-anchor-link" href="#functional-components">Functional Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="functional-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">View&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Button&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/components&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">useState&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Counter&lt;/span>: &lt;span class="kt">React.FC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">count&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setCount&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">increment&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">count&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">showToast&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showToast&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`Count: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">count&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;none&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;counter&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;count-text&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">Count&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">count&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">increment&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>&lt;span class="nx">Increment&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">showToast&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>&lt;span class="nx">Show&lt;/span> &lt;span class="nx">Toast&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">Counter&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="class-components">
&lt;a class="heading-anchor-link" href="#class-components">Class Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="class-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Component&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">View&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Input&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/components&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">State&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inputValue&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">todos&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">TodoList&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">Component&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">{},&lt;/span> &lt;span class="nx">State&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">state&lt;/span>: &lt;span class="kt">State&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inputValue&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">todos&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">componentDidMount() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Component mounted&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">handleInputChange&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setState&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inputValue&lt;/span>: &lt;span class="kt">e.detail.value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">addTodo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">inputValue&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">todos&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">inputValue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setState&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">todos&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[...&lt;/span>&lt;span class="nx">todos&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">inputValue&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inputValue&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">inputValue&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">todos&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;todo-list&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;input-section&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">inputValue&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">onInput&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handleInputChange&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">placeholder&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Enter todo...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addTodo&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>&lt;span class="nx">Add&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;todos&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">todos&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">todo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;todo-item&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">todo&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">))}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">TodoList&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="platform-apis">
&lt;a class="heading-anchor-link" href="#platform-apis">Platform APIs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="platform-apis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="navigation">
&lt;a class="heading-anchor-link" href="#navigation">Navigation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="navigation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Navigate to new page
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">navigateToDetail&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">navigateTo&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/pages/detail/detail?id=123&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Redirect to page
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">redirectToLogin&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">redirectTo&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/pages/login/login&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Navigate back
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">goBack&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">navigateBack&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">delta&lt;/span>: &lt;span class="kt">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Switch tab
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">switchToProfile&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">switchTab&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/pages/profile/profile&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="storage">
&lt;a class="heading-anchor-link" href="#storage">Storage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="storage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Set storage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">saveUserData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">userData&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setStorage&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;userData&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>: &lt;span class="kt">userData&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Data saved successfully&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Failed to save data:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Get storage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">getUserData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getStorage&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;userData&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Failed to get data:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Remove storage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">clearUserData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">removeStorage&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;userData&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Data cleared&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Failed to clear data:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="network-requests">
&lt;a class="heading-anchor-link" href="#network-requests">Network Requests&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="network-requests"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// GET request
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">fetchUserList&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;https://api.example.com/users&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;GET&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/json&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">statusCode&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Request failed&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Network error:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showToast&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Network error&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;none&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// POST request
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">createUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">userData&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;https://api.example.com/users&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;POST&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>: &lt;span class="kt">userData&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">header&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/json&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Failed to create user:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// File upload
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">uploadImage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">uploadFile&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;https://api.example.com/upload&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filePath&lt;/span>: &lt;span class="kt">filePath&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;file&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">formData&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">userId&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;123&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Upload failed:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="state-management">
&lt;a class="heading-anchor-link" href="#state-management">State Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="state-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="using-redux">
&lt;a class="heading-anchor-link" href="#using-redux">Using Redux&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-redux"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install @reduxjs/toolkit react-redux
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// store/index.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">configureStore&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@reduxjs/toolkit&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">counterSlice&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./counterSlice&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">configureStore&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reducer&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">counter&lt;/span>: &lt;span class="kt">counterSlice&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">type&lt;/span> &lt;span class="nx">RootState&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ReturnType&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">typeof&lt;/span> &lt;span class="na">store.getState&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">type&lt;/span> &lt;span class="nx">AppDispatch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">store&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dispatch&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">store&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// store/counterSlice.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createSlice&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">PayloadAction&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@reduxjs/toolkit&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">CounterState&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">initialState&lt;/span>: &lt;span class="kt">CounterState&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>: &lt;span class="kt">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">counterSlice&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createSlice&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;counter&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">initialState&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reducers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">increment&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">decrement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">-=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">incrementByAmount&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>: &lt;span class="kt">PayloadAction&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">number&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">increment&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">decrement&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">incrementByAmount&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">counterSlice&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">actions&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">counterSlice&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reducer&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// app.tsx
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Component&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">PropsWithChildren&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Provider&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-redux&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">store&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./store&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">App&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">Component&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">PropsWithChildren&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Provider&lt;/span> &lt;span class="na">store&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">children&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Provider&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">App&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Using Redux in components
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">useSelector&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useDispatch&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-redux&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RootState&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../store&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">increment&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">decrement&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../store/counterSlice&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Counter&lt;/span>: &lt;span class="kt">React.FC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useSelector&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">state&lt;/span>: &lt;span class="kt">RootState&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">counter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">dispatch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useDispatch&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">Count&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">count&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">dispatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">increment&lt;/span>&lt;span class="p">())}&amp;gt;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">dispatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">decrement&lt;/span>&lt;span class="p">())}&amp;gt;&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="platform-specific-development">
&lt;a class="heading-anchor-link" href="#platform-specific-development">Platform-Specific Development&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="platform-specific-development"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="conditional-compilation">
&lt;a class="heading-anchor-link" href="#conditional-compilation">Conditional Compilation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conditional-compilation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">MyComponent&lt;/span>: &lt;span class="kt">React.FC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">handleClick&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// #ifdef WEAPP
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// WeChat Mini Program specific code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">wx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showModal&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;WeChat&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">content&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;This is WeChat Mini Program&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// #endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// #ifdef ALIPAY
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Alipay Mini Program specific code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">my&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">alert&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Alipay&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">content&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;This is Alipay Mini Program&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// #endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// #ifdef H5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// H5 specific code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;This is H5&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// #endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleClick&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>&lt;span class="nx">Platform&lt;/span> &lt;span class="nx">Test&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="cm">/* Conditional rendering */&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TARO_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;weapp&amp;#39;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">WeChat&lt;/span> &lt;span class="nx">Mini&lt;/span> &lt;span class="nx">Program&lt;/span> &lt;span class="nx">only&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">TARO_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;h5&amp;#39;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">H5&lt;/span> &lt;span class="nx">only&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="platform-specific-styles">
&lt;a class="heading-anchor-link" href="#platform-specific-styles">Platform-Specific Styles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="platform-specific-styles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Common styles
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nc">.container&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// WeChat Mini Program
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/* #ifdef WEAPP */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#f0f0f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* #endif */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// H5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/* #ifdef H5 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">max-width&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">750&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">margin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="ni">auto&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* #endif */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Alipay Mini Program
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/* #ifdef ALIPAY */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#1677ff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* #endif */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="custom-components">
&lt;a class="heading-anchor-link" href="#custom-components">Custom Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="creating-reusable-components">
&lt;a class="heading-anchor-link" href="#creating-reusable-components">Creating Reusable Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="creating-reusable-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// components/Card/index.tsx
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">View&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Text&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/components&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">ReactNode&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="s1">&amp;#39;./index.scss&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">CardProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title?&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">children&lt;/span>: &lt;span class="kt">ReactNode&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onClick&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Card&lt;/span>: &lt;span class="kt">React.FC&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">CardProps&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">children&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onClick&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card&amp;#34;&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">onClick&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card-title&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">title&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;card-content&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">children&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">Card&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// components/Card/index.scss
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nc">.card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-radius&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">box-shadow&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="nf">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="mf">.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">margin-bottom&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">overflow&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">hidden&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">&amp;amp;&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nt">title&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">font-size&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">18&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">font-weight&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">600&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-bottom&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="mh">#f0f0f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">&amp;amp;&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nt">content&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">padding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="component-with-hooks">
&lt;a class="heading-anchor-link" href="#component-with-hooks">Component with Hooks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-with-hooks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useEffect&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">View&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Text&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/components&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">UserProfile&lt;/span>: &lt;span class="kt">React.FC&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">{&lt;/span> &lt;span class="nx">userId&lt;/span>: &lt;span class="kt">number&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">userId&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setUser&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">loading&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setLoading&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">useEffect&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">fetchUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setLoading&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`https://api.example.com/users/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">userId&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;GET&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">statusCode&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Failed to fetch user:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showToast&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">title&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Failed to load user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;none&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">finally&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setLoading&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">userId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fetchUser&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">userId&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">loading&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">Loading&lt;/span>&lt;span class="p">...&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">User&lt;/span> &lt;span class="nx">not&lt;/span> &lt;span class="nx">found&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;user-profile&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;user-name&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;user-email&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">UserProfile&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="performance-optimization">
&lt;a class="heading-anchor-link" href="#performance-optimization">Performance Optimization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="performance-optimization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="code-splitting">
&lt;a class="heading-anchor-link" href="#code-splitting">Code Splitting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code-splitting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">lazy&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Suspense&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">View&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Text&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/components&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Lazy load heavy components
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">HeavyComponent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">lazy&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="kr">import&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./HeavyComponent&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">MyPage&lt;/span>: &lt;span class="kt">React.FC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">Page&lt;/span> &lt;span class="nx">Content&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Suspense&lt;/span> &lt;span class="na">fallback&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">Loading&lt;/span>&lt;span class="p">...&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">HeavyComponent&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Suspense&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="memoization">
&lt;a class="heading-anchor-link" href="#memoization">Memoization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="memoization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">memo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useMemo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useCallback&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">View&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Button&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/components&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">ItemProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">item&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">price&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onSelect&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">ExpensiveItem&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">memo&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ItemProps&lt;/span>&lt;span class="p">&amp;gt;(({&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onSelect&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">formattedPrice&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useMemo&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Expensive calculation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="sb">`$&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">price&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toFixed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">price&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">handleSelect&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useCallback&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onSelect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onSelect&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">View&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;item&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">formattedPrice&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleSelect&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>&lt;span class="nx">Select&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">View&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">ExpensiveItem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="testing">
&lt;a class="heading-anchor-link" href="#testing">Testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="unit-testing-with-jest">
&lt;a class="heading-anchor-link" href="#unit-testing-with-jest">Unit Testing with Jest&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="unit-testing-with-jest"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev @types/jest jest ts-jest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// jest.config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">preset&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;ts-jest&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">testEnvironment&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;jsdom&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">moduleNameMapping&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;^@/(.*)$&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/src/$1&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setupFilesAfterEnv&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/src/setupTests.ts&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// __tests__/Counter.test.tsx
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fireEvent&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@testing-library/react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Counter&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../Counter&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Counter Component&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;renders initial count&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">getByText&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">Counter&lt;/span> &lt;span class="p">/&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getByText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Count: 0&amp;#39;&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toBeInTheDocument&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;increments count when button clicked&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">getByText&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">Counter&lt;/span> &lt;span class="p">/&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">button&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">getByText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Increment&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fireEvent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">click&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">button&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getByText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Count: 1&amp;#39;&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toBeInTheDocument&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="deployment">
&lt;a class="heading-anchor-link" href="#deployment">Deployment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="deployment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="build-for-different-platforms">
&lt;a class="heading-anchor-link" href="#build-for-different-platforms">Build for Different Platforms&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-for-different-platforms"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build for WeChat Mini Program&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run build:weapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build for Alipay Mini Program &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run build:alipay
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build for H5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run build:h5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build for React Native&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run build:rn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="wechat-mini-program-deployment">
&lt;a class="heading-anchor-link" href="#wechat-mini-program-deployment">WeChat Mini Program Deployment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wechat-mini-program-deployment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Development build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run dev:weapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Production build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run build:weapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Open WeChat Developer Tools&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Import the dist folder as project&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="h5-deployment">
&lt;a class="heading-anchor-link" href="#h5-deployment">H5 Deployment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="h5-deployment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// config/prod.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">env&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">NODE_ENV&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#34;production&amp;#34;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">defineConstants&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">API_BASE_URL&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#34;https://api.production.com&amp;#34;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">h5&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">publicPath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/myapp/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">staticDirectory&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">router&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;browser&amp;#39;&lt;/span> &lt;span class="c1">// or &amp;#39;hash&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="best-practices">
&lt;a class="heading-anchor-link" href="#best-practices">Best Practices&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="best-practices"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="1-component-design">
&lt;a class="heading-anchor-link" href="#1-component-design">1. Component Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-component-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Good - Single responsibility
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">UserAvatar&lt;/span>: &lt;span class="kt">React.FC&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">src&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">size&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="s1">&amp;#39;small&amp;#39;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="s1">&amp;#39;medium&amp;#39;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="s1">&amp;#39;large&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onClick&lt;/span>&lt;span class="o">?:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">src&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;medium&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onClick&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">className&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`avatar avatar-&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">size&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Image&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">className&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">onClick&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Good - Composition over inheritance
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">UserCard&lt;/span>: &lt;span class="kt">React.FC&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">{&lt;/span> &lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Card&lt;/span> &lt;span class="na">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;User Profile&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">UserAvatar&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">avatar&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;large&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;{&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">Text&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Card&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-error-handling">
&lt;a class="heading-anchor-link" href="#2-error-handling">2. Error Handling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-error-handling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useEffect&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Taro&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@tarojs/taro&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">useApiData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nx">url&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setData&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">loading&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setLoading&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setError&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">string&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">useEffect&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">fetchData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setLoading&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">statusCode&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`HTTP &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">statusCode&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span> &lt;span class="k">instanceof&lt;/span> &lt;span class="nb">Error&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">message&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Unknown error&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">finally&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setLoading&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fetchData&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-type-safety">
&lt;a class="heading-anchor-link" href="#3-type-safety">3. Type Safety&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-type-safety"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Define types for better development experience
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">interface&lt;/span> &lt;span class="nx">ApiResponse&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">code&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">message&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>: &lt;span class="kt">T&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">avatar?&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">PageProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">userId&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">UserDetailPage&lt;/span>: &lt;span class="kt">React.FC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">userId&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Taro&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getCurrentInstance&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">router&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">PageProps&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">data&lt;/span>: &lt;span class="kt">user&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useApiData&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">`/api/users/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">userId&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Type-safe component logic
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Taro provides a powerful solution for cross-platform mini program development. Key benefits include:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Code Reusability&lt;/strong>: Write once, deploy everywhere&lt;/li>
&lt;li>&lt;strong>Familiar Syntax&lt;/strong>: React-like development experience&lt;/li>
&lt;li>&lt;strong>Rich Ecosystem&lt;/strong>: Comprehensive tooling and community support&lt;/li>
&lt;li>&lt;strong>Performance&lt;/strong>: Optimized compilation and runtime&lt;/li>
&lt;li>&lt;strong>Type Safety&lt;/strong>: Built-in TypeScript support&lt;/li>
&lt;/ol>
&lt;p>While Taro handles most platform differences automatically, understanding platform-specific features and limitations is crucial for building robust applications. Start with simple components and gradually explore advanced features as your application grows.&lt;/p>
&lt;blockquote>
&lt;p>Recently, I had a mini program development project. Since I&amp;rsquo;ve been using React as my main technology for the past few years, after weighing the options, I decided to use Taro because it supports React for development. Here, I&amp;rsquo;ve accumulated some experience and summarized it for future reference.&lt;/p>
&lt;/blockquote>
&lt;h2 id="get-started">
&lt;a class="heading-anchor-link" href="#get-started">Get Started&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="get-started"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm i -g @tarojs/cli
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">taro init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If you plan to use taro-ui in the future, you need to select Sass because taro-ui uses Sass.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="ui-component-libraries">
&lt;a class="heading-anchor-link" href="#ui-component-libraries">UI Component Libraries&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ui-component-libraries"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://taro-ui.jd.com/" target="_blank" rel="noopener">taro-ui&lt;/a>
I also researched other options. Considering the convenience of maintenance and use, I ultimately chose taro-ui.&lt;/li>
&lt;/ul>
&lt;h2 id="some-libraries">
&lt;a class="heading-anchor-link" href="#some-libraries">Some Libraries&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="some-libraries"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>For form state management, you can use &lt;code>react-hook-form&lt;/code>&lt;/li>
&lt;li>For state management, you can use &lt;code>redux&lt;/code>&lt;/li>
&lt;li>For network requests, you can use &lt;code>axios&lt;/code>&lt;/li>
&lt;li>For QR code generation, you can use &lt;a href="https://github.com/Miaonster/taro-code" target="_blank" rel="noopener">&lt;code>taro-code&lt;/code>&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.taro.zone/docs/" target="_blank" rel="noopener">https://docs.taro.zone/docs/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://taro-ui.jd.com/" target="_blank" rel="noopener">https://taro-ui.jd.com/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>The time-style Parameter in Linux ls</title><link>https://en.1991421.cn/2023/09/05/linuxlstime-style/</link><pubDate>Tue, 05 Sep 2023 21:40:18 +0800</pubDate><guid>https://en.1991421.cn/2023/09/05/linuxlstime-style/</guid><description>&lt;blockquote>
&lt;p>While building WebShell, I needed a reliable way to get file lists and metadata from ls. Here&amp;rsquo;s a note.&lt;/p>
&lt;/blockquote>
&lt;h2 id="command">
&lt;a class="heading-anchor-link" href="#command">Command&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="command"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ls -Al --time-style&lt;span class="o">=&lt;/span>full-iso
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output looks like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">total &lt;span class="m">190600&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r-- &lt;span class="m">1&lt;/span> root root &lt;span class="m">1781&lt;/span> 2023-07-24 09:56:56.520113036 +0800 cosfs.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxrwxrw- &lt;span class="m">3&lt;/span> root root &lt;span class="m">4096&lt;/span> 2023-06-05 16:32:39.466006640 +0800 dir1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxrwxrw- &lt;span class="m">2&lt;/span> root root &lt;span class="m">4096&lt;/span> 2023-05-27 20:46:06.219707419 +0800 dir2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lrwxrwxrwx &lt;span class="m">1&lt;/span> root root &lt;span class="m">4&lt;/span> 2023-09-05 21:45:49.401943527 +0800 dir3 -&amp;gt; dir1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">3&lt;/span> root root &lt;span class="m">4096&lt;/span> 2023-05-06 06:57:38.000000000 +0800 docs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">484&lt;/span> root root &lt;span class="m">20480&lt;/span> 2023-05-06 15:42:58.494411265 +0800 node_modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">4&lt;/span> root root &lt;span class="m">4096&lt;/span> 2023-08-29 11:07:33.162370076 +0800 .orca_term
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-rw-r--r-- &lt;span class="m">1&lt;/span> root root &lt;span class="m">195132152&lt;/span> 2023-06-14 19:04:12.167000570 +0800 &lt;span class="nb">test&lt;/span> 2.zip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>From this output, you can parse:&lt;/p>
&lt;ol>
&lt;li>Permissions&lt;/li>
&lt;li>Owner&lt;/li>
&lt;li>Group&lt;/li>
&lt;li>File size&lt;/li>
&lt;li>ISO time with timezone&lt;/li>
&lt;li>File name (and symlink target if &lt;code>-&amp;gt;&lt;/code>)&lt;/li>
&lt;/ol>
&lt;h2 id="limitations--compatibility">
&lt;a class="heading-anchor-link" href="#limitations--compatibility">Limitations / compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="limitations--compatibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>time-style&lt;/code> is not supported on all Linux distributions, and Unix-like systems may not support it either.&lt;/p>
&lt;p>Examples:&lt;/p>
&lt;ol>
&lt;li>Alpine Linux does not support it and has no built-in equivalent.&lt;/li>
&lt;li>Unix-like systems such as FreeBSD do not support it.&lt;/li>
&lt;/ol>
&lt;h2 id="workarounds">
&lt;a class="heading-anchor-link" href="#workarounds">Workarounds&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="workarounds"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Install &lt;code>coreutils&lt;/code>. On FreeBSD: &lt;code>pkg install coreutils&lt;/code>. On Alpine: &lt;code>apk add coreutils&lt;/code>.&lt;/li>
&lt;li>Avoid &lt;code>time-style&lt;/code> and combine &lt;code>stat&lt;/code> instead. The downside is worse performance due to extra commands.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Linux has many distributions. Be careful about compatibility, which depends on command versions and Linux/Unix standards.&lt;/p></description></item><item><title>Terminal Search in WebShell</title><link>https://en.1991421.cn/2023/08/21/webshell/</link><pubDate>Mon, 21 Aug 2023 11:58:25 +0800</pubDate><guid>https://en.1991421.cn/2023/08/21/webshell/</guid><description>&lt;h3 id="install">
&lt;a class="heading-anchor-link" href="#install">install&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm i xterm-addon-search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">const &lt;span class="nv">searchAddon&lt;/span> &lt;span class="o">=&lt;/span> new SearchAddon&lt;span class="o">()&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">term.loadAddon&lt;span class="o">(&lt;/span>searchAddon&lt;span class="o">)&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>On the alternate screen (e.g., within &lt;code>vi&lt;/code>), search results may be inaccurate, since the content loaded by &lt;code>vi&lt;/code> isn’t necessarily the full file. For commands like &lt;code>cat&lt;/code>, terminal-level search works fine. On the alternate screen, prefer the editor’s built-in search.&lt;/p>
&lt;p>Determine whether the terminal is in normal or alternate screen with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">active&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;normal&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To listen for changes, use &lt;code>term.onBufferChange&lt;/code>.&lt;/p></description></item><item><title>How to Use monaco-editor (Step-by-Step Guide)</title><link>https://en.1991421.cn/2023/07/09/monaco-editor/</link><pubDate>Sun, 09 Jul 2023 13:38:31 +0800</pubDate><guid>https://en.1991421.cn/2023/07/09/monaco-editor/</guid><description>&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm i react-monaco-editor monaco-editor
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm i monaco-editor-webpack-plugin -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="webpack-bundling">
&lt;a class="heading-anchor-link" href="#webpack-bundling">Webpack Bundling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack-bundling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nx">MonacoWebpackPlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// available options are documented at https://github.com/microsoft/monaco-editor/blob/main/webpack-plugin/README.md#options
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">languages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;shell&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;json&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;xml&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;yaml&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;dockerfile&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;bat&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;ini&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;markdown&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;javascript&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filename&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/monaco-editor/[name].worker.[contenthash].js&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">publicPath&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">features&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;coreCommands&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">globalAPI&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: the &lt;code>filename&lt;/code> config only affects the main file. The dynamically loaded language module filenames are not affected by this setting. The workaround is as follows.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">output&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">chunkFilename&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">chunk&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">regex&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sr">/((?&amp;lt;=esm_)vs_(basic-languages|language)_.+)/&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">language&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">chunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">regex&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">language&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="sb">`vendor/monaco-&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">language&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">.[chunkhash].js`&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;vendor/[name].[chunkhash].js&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">optimization&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">chunkIds&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;named&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/microsoft/monaco-editor/discussions/3769" target="_blank" rel="noopener">https://github.com/microsoft/monaco-editor/discussions/3769&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use xterm-addon-web-links (Step-by-Step Guide)</title><link>https://en.1991421.cn/2023/07/06/xterm-addon-web-links/</link><pubDate>Thu, 06 Jul 2023 23:31:24 +0800</pubDate><guid>https://en.1991421.cn/2023/07/06/xterm-addon-web-links/</guid><description>&lt;blockquote>
&lt;p>The &lt;code>xterm-addon-web-links&lt;/code> plugin can be used to implement link recognition and clicking in xterm.js terminal content. Configuration is straightforward, but there are some detail issues. Marking them down here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="keeping-focus-on-original-tab">
&lt;a class="heading-anchor-link" href="#keeping-focus-on-original-tab">Keeping Focus on Original Tab&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="keeping-focus-on-original-tab"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">term&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">loadAddon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">WebLinksAddon&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">WebLinksAddon&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">uri&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">isMac&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">metaKey&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ctrlKey&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">uri&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Browsers have two behaviors for link clicks:&lt;/p>
&lt;ol>
&lt;li>LeftClick directly opens a new tab with focus on the new page&lt;/li>
&lt;li>MetaKey+LeftClick opens a new tab but keeps focus on the original page
&lt;ul>
&lt;li>⌘ on Mac&lt;/li>
&lt;li>Ctrl on Windows&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Since my requirement is to not lose focus when clicking links, I can only add metaKey judgment in the click event to ensure focus doesn&amp;rsquo;t get lost when users trigger clicks.&lt;/p>
&lt;h2 id="can-ctrl-be-used-instead-of-meta-on-mac">
&lt;a class="heading-anchor-link" href="#can-ctrl-be-used-instead-of-meta-on-mac">Can Ctrl be Used Instead of Meta on Mac?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="can-ctrl-be-used-instead-of-meta-on-mac"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Ctrl+LeftClick on Mac behaves equivalent to RightClick (right-click menu), so it&amp;rsquo;s not possible.&lt;/p>
&lt;h2 id="providing-tooltips-on-hover">
&lt;a class="heading-anchor-link" href="#providing-tooltips-on-hover">Providing Tooltips on Hover&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="providing-tooltips-on-hover"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If users don&amp;rsquo;t know about ⌘+LeftClick for link clicking, we can provide tooltip information when hovering over detected links.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hover&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">location&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">location&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The second parameter of WebLinksAddon is a configuration parameter that includes a hover callback function. You can get cursor position information from the event, so you can use the position information to create tooltips or similar displays.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That&amp;rsquo;s it!&lt;/p></description></item><item><title>AntiDebug Implementation</title><link>https://en.1991421.cn/2023/06/24/antidebug/</link><pubDate>Sat, 24 Jun 2023 13:52:24 +0800</pubDate><guid>https://en.1991421.cn/2023/06/24/antidebug/</guid><description>&lt;blockquote>
&lt;p>Recently while using Wenxin Yiyan (Baidu&amp;rsquo;s AI), I opened the developer tools and found that it entered a breakpoint in an anonymous function. When clicking to continue execution, the page directly turned into a blank tab. This is clearly an anti-debugging measure. Here I&amp;rsquo;ll analyze this implementation technique.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-06-24-154933.jpg"
alt="https://static.1991421.cn/2023/2023-06-24-154933.jpg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Examining the call stack at the breakpoint revealed setInterval.&lt;/li>
&lt;li>The tag for the anonymous function is displayed as VM, so the anonymous function containing the breakpoint is code generated by eval or new Function.&lt;/li>
&lt;li>After the breakpoint executes, it runs window.location.replace(&amp;ldquo;about:blank&amp;rdquo;);. I couldn&amp;rsquo;t determine exactly how it detects entering a breakpoint because the code is obfuscated, but one approach is to use time difference detection. When entering a debugger, the logic execution will be significantly delayed, so calculating a time difference can determine if developer tools are open.&lt;/li>
&lt;/ol>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">startTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">performance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">eval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(function() { debugger;})()&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">stopTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">performance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nx">stopTime&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nx">startTime&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;about:blank&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">400&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="xinghuas-approach">
&lt;a class="heading-anchor-link" href="#xinghuas-approach">Xinghua&amp;rsquo;s Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="xinghuas-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I looked at Xinghua (iFlytek&amp;rsquo;s AI) and found they also implemented anti-crawling measures, but with a different approach. Xinghua uses the fact that opening developer tools compresses the webpage&amp;rsquo;s height or width, and detects this difference to make a determination. However, this approach has a drawback - if the developer tools are opened in a separate window, this detection becomes ineffective.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-06-24-161454.jpeg"
alt="https://static.1991421.cn/2023/2023-06-24-161454.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">widthThreshold&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">outerWidth&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">innerWidth&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="nx">threshold&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">heightThreshold&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">outerHeight&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">innerHeight&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="nx">threshold&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">orientation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">widthThreshold&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;vertical&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;horizontal&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="counter-antidebug">
&lt;a class="heading-anchor-link" href="#counter-antidebug">Counter-AntiDebug&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="counter-antidebug"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Having roughly analyzed the implementation methods, the corresponding solutions are quite simple. For Xinghua&amp;rsquo;s approach, as mentioned earlier, simply using developer tools in a separate window breaks their detection.&lt;/p>
&lt;p>For Baidu&amp;rsquo;s method which uses timers, we can simply override the timer implementation. Using &lt;strong>Tampermonkey&lt;/strong> is recommended:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setInterval&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">callback&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">delay&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Here, we simply don&amp;#39;t call the original setInterval function, thus blocking all timers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Blocked a setInterval call with delay &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">delay&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="never-pause-here">
&lt;a class="heading-anchor-link" href="#never-pause-here">Never Pause Here&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="never-pause-here"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-08-17-232218.jpeg"
alt="https://static.1991421.cn/2023/2023-08-17-232218.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>From a purely technical perspective, AntiDebug is interesting - for example, understanding the trigger mechanism of debuggers, learning HACK techniques to detect when developer tools are open, and how to break AntiDebug implementations.&lt;/li>
&lt;li>Setting aside the technical aspects, why are &amp;ldquo;self-developed&amp;rdquo; AIs like Baidu and Xinghua afraid of frontend debugging? What are they 100% nervous about?&lt;/li>
&lt;/ol>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://x-c3ll.github.io/posts/javascript-antidebugging/" target="_blank" rel="noopener">https://x-c3ll.github.io/posts/javascript-antidebugging/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Upgrade Blog Theme: 2.0</title><link>https://en.1991421.cn/2023/06/03/upgrade-blog-theme-2-0/</link><pubDate>Sat, 03 Jun 2023 13:39:23 +0800</pubDate><guid>https://en.1991421.cn/2023/06/03/upgrade-blog-theme-2-0/</guid><description>&lt;blockquote>
&lt;p>I recently did a second round of blog upgrades to keep technical debt from piling up and becoming painful later.&lt;/p>
&lt;p>Quick notes on what changed.&lt;/p>
&lt;/blockquote>
&lt;h2 id="upgrade-the-hexo-framework">
&lt;a class="heading-anchor-link" href="#upgrade-the-hexo-framework">Upgrade the Hexo Framework&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrade-the-hexo-framework"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check and update Hexo, hexo-cli, and related packages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm-check -u
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Hexo 7 is still in RC, and the NexT theme depends on 6.x right now, so I’m holding off on a major upgrade.&lt;/p>
&lt;h2 id="upgrade-the-next-theme">
&lt;a class="heading-anchor-link" href="#upgrade-the-next-theme">Upgrade the NexT Theme&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrade-the-next-theme"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I used to delete the folder and copy in the new version. Now I install it as an NPM package instead — much easier.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm install hexo-theme-next
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This bump moves from &lt;code>7.8.0&lt;/code> to &lt;code>8.17.0&lt;/code>.&lt;/p>
&lt;p>The release includes a breaking change: move &lt;code>source/_data/next.yml&lt;/code> to the project root and rename it &lt;code>_config.next.yml&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>There are two repos: &lt;code>next-theme/hexo-theme-next&lt;/code> and &lt;code>iissnan/hexo-theme-next&lt;/code>. Use &lt;code>next-theme/hexo-theme-next&lt;/code>; that’s the one publishing to npm.&lt;/p>
&lt;/blockquote>
&lt;h3 id="enable-addtoany">
&lt;a class="heading-anchor-link" href="#enable-addtoany">Enable AddToAny&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-addtoany"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>AddThis has shut down, so I switched the sharing service over to AddToAny.&lt;/p>
&lt;p>That wraps this round of upgrades.&lt;/p></description></item><item><title>Deploy a Personal chatgpt-telegram-bot</title><link>https://en.1991421.cn/2023/04/26/chatgpt-telegram-bot/</link><pubDate>Wed, 26 Apr 2023 23:18:44 +0800</pubDate><guid>https://en.1991421.cn/2023/04/26/chatgpt-telegram-bot/</guid><description>&lt;blockquote>
&lt;p>ChatGPT usage can be unstable. To avoid downtime impacting work, I set up a private Telegram bot as a backup. After some research, I chose to deploy the excellent &lt;a href="https://github.com/n3d1117/chatgpt-telegram-bot" target="_blank" rel="noopener">chatgpt-telegram-bot&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-04-26-231541.jpeg"
alt="https://static.1991421.cn/2023/2023-04-26-231541.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="setup-steps">
&lt;a class="heading-anchor-link" href="#setup-steps">Setup steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>In Telegram, talk to @BotFather to create a bot.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>In Telegram, talk to @useridinfobot to get your user ID (not your display name).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Choose a HK or overseas server and pull the image.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker pull n3d1117/chatgpt-telegram-bot:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Create a &lt;code>.env&lt;/code> file and set environment variables. Reference: &lt;a href="https://github.com/n3d1117/chatgpt-telegram-bot/blob/main/.env.example" target="_blank" rel="noopener">https://github.com/n3d1117/chatgpt-telegram-bot/blob/main/.env.example&lt;/a>&lt;/p>
&lt;p>The four main settings:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># Your OpenAI API key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OPENAI_API_KEY=XXX
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Your Telegram bot token obtained using @BotFather
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">TELEGRAM_BOT_TOKEN=XXX
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Telegram user ID of admins, or - to assign no admin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ADMIN_USER_IDS=ADMIN_1_USER_ID,ADMIN_2_USER_ID
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Comma separated list of telegram user IDs, or * to allow all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ALLOWED_TELEGRAM_USER_IDS=USER_ID_1,USER_ID_2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Start the container&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker run -d --name &lt;span class="s2">&amp;#34;chatgpt-telegram-bot&amp;#34;&lt;/span> --env-file ./.env n3d1117/chatgpt-telegram-bot:latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Now you can talk to your bot in Telegram.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="autorestart-on-config-changes">
&lt;a class="heading-anchor-link" href="#autorestart-on-config-changes">Auto‑restart on config changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="autorestart-on-config-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With a plain &lt;code>docker run&lt;/code> container, updating the &lt;code>.env&lt;/code> file won’t auto‑reload the container. To recreate it on changes, I used &lt;code>inotify-tools&lt;/code> (CentOS 7 example below):&lt;/p>
&lt;ol>
&lt;li>&lt;code>sudo yum -y install inotify-tools&lt;/code>&lt;/li>
&lt;li>Create &lt;code>watch_env.sh&lt;/code>&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">while&lt;/span> true&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> inotifywait -e modify .env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;.env file has been modified. Restarting the container...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> docker stop chatgpt-telegram-bot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> docker rm chatgpt-telegram-bot
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> docker run -d --name &lt;span class="s2">&amp;#34;chatgpt-telegram-bot&amp;#34;&lt;/span> --env-file /root/chatgpt-telegram-bot/.env n3d1117/chatgpt-telegram-bot:latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Create &lt;code>watch-env.service&lt;/code>&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo vi /etc/systemd/system/watch-env.service
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Service configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[Unit]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Description=Watch and restart Docker container on .env file changes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[Service]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Type=simple
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ExecStart=/bin/bash /root/chatgpt-telegram-bot/watch_env.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Restart=always
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">User=root
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[Install]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">WantedBy=multi-user.target
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>Enable and start the service&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo systemctl daemon-reload
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> watch-env.service
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl start watch-env.service
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="gpt4">
&lt;a class="heading-anchor-link" href="#gpt4">GPT‑4&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gpt4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After getting access approval, set &lt;code>OPENAI_MODEL=gpt-4&lt;/code> in &lt;code>.env&lt;/code>.&lt;/p>
&lt;p>Apply here: &lt;a href="https://openai.com/waitlist/gpt-4-api" target="_blank" rel="noopener">https://openai.com/waitlist/gpt-4-api&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s it — a personal bot is up. It’s paid, but relatively stable. Hoping to get API access for GPT‑4 soon to make it even better.&lt;/p></description></item><item><title>Reading the ssh2 HTTPAgent Source Code</title><link>https://en.1991421.cn/2023/04/19/ssh2-httpagent/</link><pubDate>Wed, 19 Apr 2023 23:24:21 +0800</pubDate><guid>https://en.1991421.cn/2023/04/19/ssh2-httpagent/</guid><description>&lt;blockquote>
&lt;p>The &lt;code>ssh2&lt;/code> package ships HTTP agents that intercept requests and forward them through an SSH connection. I recently needed a custom agent, so I dug through the implementation.&lt;/p>
&lt;/blockquote>
&lt;p>The agent lives in &lt;code>lib/http-agents.js&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Agent&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">HttpAgent&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Agent&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">HttpsAgent&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">ctor&lt;/span> &lt;span class="k">of&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">HttpAgent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HttpsAgent&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">exports&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">ctor&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">HttpAgent&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;SSHTTPAgent&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;SSHTTPSAgent&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">SSHAgent&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A loop adapts Node’s built-in &lt;code>Agent&lt;/code> constructors and exports derived classes.&lt;/p>
&lt;p>Node’s &lt;code>Agent&lt;/code> signature:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nx">Agent&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nx">options&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>ssh2’s agent accepts two arguments because it also needs SSH configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connectCfg&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">agentOptions&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">super&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">agentOptions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_connectCfg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">connectCfg&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_defaultSrcIP&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">agentOptions&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">agentOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">srcIP&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="s1">&amp;#39;localhost&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>connectCfg&lt;/code> holds SSH connection parameters.&lt;/li>
&lt;li>&lt;code>agentOptions&lt;/code> is passed to Node’s &lt;code>Agent&lt;/code>. ssh2 adds &lt;code>srcIP&lt;/code>, controlling the source IP for the TCP socket.&lt;/li>
&lt;/ul>
&lt;p>&lt;code>createConnection(options, cb)&lt;/code> is invoked by the pooling mechanism to obtain a TCP socket. The agent:&lt;/p>
&lt;ol>
&lt;li>Creates an SSH &lt;code>Client&lt;/code> and connects using &lt;code>connectCfg&lt;/code>.&lt;/li>
&lt;li>On &lt;code>ready&lt;/code>, calls &lt;code>forwardOut&lt;/code> to open a TCP stream.&lt;/li>
&lt;li>Calls &lt;code>cb(null, decorateStream(stream, ctor, options));&lt;/code> so the stream exposes the methods expected by HTTP(S) consumers.&lt;/li>
&lt;/ol>
&lt;p>Error-handling highlights:&lt;/p>
&lt;ol>
&lt;li>If the TCP communication errors, the error is passed to &lt;code>cb&lt;/code> and the SSH client closes.&lt;/li>
&lt;li>If the TCP connection closes, the SSH client closes; no error is passed to &lt;code>cb&lt;/code> because the closure is expected.&lt;/li>
&lt;/ol>
&lt;p>When creating the local endpoint, a port must be bound. If &lt;code>localPort&lt;/code> isn’t supplied, it defaults to &lt;code>0&lt;/code>, letting the OS choose an ephemeral port (not literally port 0):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">srcPort&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">options&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">localPort&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Implementing an Editor Proxy in WebShell</title><link>https://en.1991421.cn/2023/04/05/webshell/</link><pubDate>Wed, 05 Apr 2023 13:16:34 +0800</pubDate><guid>https://en.1991421.cn/2023/04/05/webshell/</guid><description>&lt;blockquote>
&lt;p>I added an editor feature to WebShell last year. It’s finally stable now. Here’s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="research">
&lt;a class="heading-anchor-link" href="#research">Research&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="research"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Few terminal products ship an editor; most only provide SFTP.&lt;/li>
&lt;li>Google&lt;/li>
&lt;/ol>
&lt;h2 id="architecture">
&lt;a class="heading-anchor-link" href="#architecture">Architecture&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="architecture"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-04-05-132407.jpeg"
alt="https://static.1991421.cn/2023/2023-04-05-132407.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="implementation">
&lt;a class="heading-anchor-link" href="#implementation">Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Key steps:&lt;/p>
&lt;ol>
&lt;li>On the server, SSH into the target machine and install/run code-server&lt;/li>
&lt;li>Use SSH port forwarding to expose the internal code-server service&lt;/li>
&lt;li>Run a proxy server to receive browser requests and forward them to the user’s machine, reaching code-server&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>done!&lt;/p></description></item><item><title>Frontend Image Compression in WebShell</title><link>https://en.1991421.cn/2023/04/05/frontend-image-compression/</link><pubDate>Wed, 05 Apr 2023 11:57:39 +0800</pubDate><guid>https://en.1991421.cn/2023/04/05/frontend-image-compression/</guid><description>&lt;blockquote>
&lt;p>I recently researched frontend image compression and summed it up here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="mime">
&lt;a class="heading-anchor-link" href="#mime">MIME&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mime"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;strong>media type&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;file&amp;#34;&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;selectImg&amp;#34;&lt;/span> &lt;span class="na">accept&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image/*&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>For example, to restrict an upload picker so non-image files can’t be selected, use &lt;code>image/*&lt;/code>.&lt;/li>
&lt;li>This restriction can be bypassed on Windows, so add stricter MIME checks in the submit logic/back end as well.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">startsWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;image/&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="compression-methods">
&lt;a class="heading-anchor-link" href="#compression-methods">Compression methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compression-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Dimensions&lt;/p>
&lt;p>Reduce file size by lowering the pixel dimensions.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Quality&lt;/p>
&lt;p>Lower quality by trading off detail and fidelity.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="format-comparison">
&lt;a class="heading-anchor-link" href="#format-comparison">Format comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="format-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-04-05-124346.jpeg"
alt="https://static.1991421.cn/2023/2023-04-05-124346.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="compatibility">
&lt;a class="heading-anchor-link" href="#compatibility">Compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>JPEG 2000&lt;/strong> by the JPEG group, the oldest of the JPEG successors, available in Safari&lt;/li>
&lt;li>&lt;strong>WebP&lt;/strong> by Google, available in all browsers&lt;/li>
&lt;li>&lt;strong>HEIC&lt;/strong> by the MPEG group, based on HEVC and available in iOS
&lt;ul>
&lt;li>Unsupported by all major browsers&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>AVIF&lt;/strong> by the Alliance for Open Media (AOM), available in Chrome and Firefox&lt;/li>
&lt;li>&lt;strong>JPEG XL&lt;/strong> by the JPEG group, the next-generation codec&lt;/li>
&lt;li>&lt;strong>WebP2&lt;/strong> by Google, an experimental successor to WebP&lt;/li>
&lt;/ul>
&lt;h2 id="is-frontend-compression-feasible">
&lt;a class="heading-anchor-link" href="#is-frontend-compression-feasible">Is frontend compression feasible?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-frontend-compression-feasible"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>canvas&lt;/li>
&lt;/ul>
&lt;h2 id="compressorjs">
&lt;a class="heading-anchor-link" href="#compressorjs">Compressor.js&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compressorjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nx">Compressor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">selectedImg&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">strict&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">quality&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mf">0.6&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">maxHeight&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">2160&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">maxWidth&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">3840&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">convertSize&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">2000000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mimeType&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;auto&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">success&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="gracefully-fallback-images">
&lt;a class="heading-anchor-link" href="#gracefully-fallback-images">Gracefully fallback images&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gracefully-fallback-images"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="html-img">
&lt;a class="heading-anchor-link" href="#html-img">HTML &lt;img>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="html-img"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;111.avif&amp;#34;&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image/avif&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;111.webp&amp;#34;&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;image/webp&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;111.png&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="css-background-image">
&lt;a class="heading-anchor-link" href="#css-background-image">CSS background-image&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="css-background-image"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>There’s no great pure-CSS option today. Use JS to detect AVIF/WebP support and choose the right image at runtime.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">isAvifSupported&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">image&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Image&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">image&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onload&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">image&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onerror&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">image&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">src&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Beware: some @supports checks can be misleading. On macOS Big Sur + Safari 16, it “matched” but AVIF wasn’t actually supported, causing black screens.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Takeaways:&lt;/p>
&lt;ol>
&lt;li>Use AVIF/WebP when compatible; fall back as needed.&lt;/li>
&lt;li>Frontend can pre‑compress non‑animated formats when appropriate.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://cloudinary.com/blog/time_for_next_gen_codecs_to_dethrone_jpeg" target="_blank" rel="noopener">https://cloudinary.com/blog/time_for_next_gen_codecs_to_dethrone_jpeg&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Custom Terminal Background Images in WebShell</title><link>https://en.1991421.cn/2023/03/06/webshell/</link><pubDate>Mon, 06 Mar 2023 23:50:40 +0800</pubDate><guid>https://en.1991421.cn/2023/03/06/webshell/</guid><description>&lt;blockquote>
&lt;p>WebShell recently added support for custom background images. Here’s the overall approach.&lt;/p>
&lt;/blockquote>
&lt;h2 id="architecture">
&lt;a class="heading-anchor-link" href="#architecture">Architecture&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="architecture"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-03-06-235223.jpeg"
alt="https://static.1991421.cn/2023/2023-03-06-235223.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As the diagram shows, the implementation is roughly:&lt;/p>
&lt;ol>
&lt;li>xterm.js supports transparent backgrounds; set the terminal background to transparent.&lt;/li>
&lt;li>Add a sibling DOM element in front of the terminal and apply a background image to it via CSS. This becomes the visual “bottom layer” beneath the terminal, with opacity controlling transparency.&lt;/li>
&lt;li>Set the terminal’s parent element to a solid color without transparency.&lt;/li>
&lt;/ol>
&lt;p>With the above, you get a custom terminal background image with adjustable transparency.&lt;/p>
&lt;h2 id="demo">
&lt;a class="heading-anchor-link" href="#demo">Demo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="demo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://github.com/alanhe421/express-demo" target="_blank" rel="noopener">Link&lt;/a>&lt;/p></description></item><item><title>Adding a PWA Shortcut to the Desktop</title><link>https://en.1991421.cn/2023/02/27/pwa-add-to-home-screen/</link><pubDate>Mon, 27 Feb 2023 21:51:19 +0800</pubDate><guid>https://en.1991421.cn/2023/02/27/pwa-add-to-home-screen/</guid><description>&lt;blockquote>
&lt;p>We wanted users to “install” the WebShell as a desktop app. The plan: implement PWA support and leverage the add-to-home-screen experience.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-02-27-220540.jpeg"
alt="https://static.1991421.cn/2023/2023-02-27-220540.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="pwa-configuration">
&lt;a class="heading-anchor-link" href="#pwa-configuration">PWA Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pwa-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="manifestwebmanifest">
&lt;a class="heading-anchor-link" href="#manifestwebmanifest">&lt;code>manifest.webmanifest&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="manifestwebmanifest"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Sample: &lt;a href="https://github.com/alanhe421/express-demo/blob/781c83b468e6214cf90fa61c66a94249b81125b3/static/xterm.webmanifest" target="_blank" rel="noopener">https://github.com/alanhe421/express-demo/blob/781c83b468e6214cf90fa61c66a94249b81125b3/static/xterm.webmanifest&lt;/a>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;code>display&lt;/code>&lt;/p>
&lt;ul>
&lt;li>&lt;code>minimal-ui&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-02-27-223043.jpeg"
alt="https://static.1991421.cn/2023/2023-02-27-223043.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>&lt;code>standalone&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-02-27-223109.jpeg"
alt="https://static.1991421.cn/2023/2023-02-27-223109.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>name&lt;/code> / &lt;code>short_name&lt;/code> / &lt;code>display&lt;/code>&lt;/p>
&lt;ul>
&lt;li>Changing &lt;code>name&lt;/code> shows an update prompt after a browser restart; &lt;code>display&lt;/code> applies immediately without a prompt.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-10-24-124206.jpeg"
alt="https://static.1991421.cn/2023/2023-10-24-124206.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>description&lt;/code>&lt;/p>
&lt;ul>
&lt;li>Shows on Windows when you hover over the shortcut—keep it short.&lt;/li>
&lt;li>Updating the manifest doesn’t refresh existing shortcuts; users must reinstall.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;code>theme_color&lt;/code>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Update it dynamically via &lt;code>&amp;lt;meta name=&amp;quot;theme-color&amp;quot; content=&amp;quot;#0066ff&amp;quot;/&amp;gt;&lt;/code> if needed.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-02-27-223225.jpeg"
alt="https://static.1991421.cn/2023/2023-02-27-223225.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="5">
&lt;li>&lt;code>icons&lt;/code>
&lt;ul>
&lt;li>Provide 192px/512px sizes; include maskable variants.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>screenshots&lt;/code>
&lt;ul>
&lt;li>Recommended sizes: &lt;code>400x822&lt;/code> (narrow) and &lt;code>1280x676&lt;/code> (wide).&lt;/li>
&lt;li>Use PNG; WebP didn’t work in our tests.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="register-the-service-worker">
&lt;a class="heading-anchor-link" href="#register-the-service-worker">Register the Service Worker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="register-the-service-worker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Example: &lt;a href="https://github.com/alanhe421/express-demo/blob/c129ffabc49757302bf3350c607c4dec71a1f29f/static/js/sw.js" target="_blank" rel="noopener">https://github.com/alanhe421/express-demo/blob/c129ffabc49757302bf3350c607c4dec71a1f29f/static/js/sw.js&lt;/a>&lt;/p>
&lt;h3 id="debugging">
&lt;a class="heading-anchor-link" href="#debugging">Debugging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="debugging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Chrome incognito disables install prompts and Service Worker logging.&lt;/p>
&lt;h2 id="compatibility">
&lt;a class="heading-anchor-link" href="#compatibility">Compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Most modern browsers support PWAs (Firefox, Safari, etc.), though install support varies. Desktop installation works on recent Edge/Chrome releases for Windows and macOS.&lt;/p>
&lt;p>Listen for &lt;code>beforeinstallprompt&lt;/code>; if it fires, installation is available.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;beforeinstallprompt&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Non-null means installation is available; null means unsupported or already installed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">deferredPrompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Some Chrome users still reported no prompt—likely due to browser settings. The approach above plus &lt;code>beforeinstallprompt&lt;/code> gets us most of the way there.&lt;/p>
&lt;p>&lt;strong>Update:&lt;/strong> Safari on macOS Sonoma now shows installation prompts.&lt;/p>
&lt;h2 id="common-questions">
&lt;a class="heading-anchor-link" href="#common-questions">Common Questions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-questions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Detecting standalone launch:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">isStandalone&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(display-mode: standalone)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;standalone&amp;#39;&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nx">navigator&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">standalone&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">)))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Detect whether the app is already installed&lt;/p>
&lt;ul>
&lt;li>&lt;code>beforeinstallprompt&lt;/code> won’t fire if it’s unsupported (Firefox), prerequisites aren’t met, or it’s already installed. There’s no perfect detection.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Programmatically open the installed app from the webpage? No API yet.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Multiple launches&lt;/p>
&lt;ul>
&lt;li>Each “Open with” click opens another window; PWAs are still browser tabs under the hood.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Custom install prompt&lt;/p>
&lt;ul>
&lt;li>You can add your own UI and trigger the browser’s prompt manually:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">installApp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;click&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">deferredPrompt&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">deferredPrompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prompt&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">outcome&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">deferredPrompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userChoice&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">outcome&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;accepted&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">deferredPrompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Splash screens? Not configurable beyond what the browser provides—simulate it with your own loading animation.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Incognito mode won’t trigger &lt;code>beforeinstallprompt&lt;/code>, though Service Workers still run.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>HTTPS&lt;/code> is mandatory for PWAs.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="example-sites">
&lt;a class="heading-anchor-link" href="#example-sites">Example Sites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-sites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Examples that support desktop install:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://www.v2ex.com/" target="_blank" rel="noopener">https://www.v2ex.com/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://youtube.com/" target="_blank" rel="noopener">https://youtube.com/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/" target="_blank" rel="noopener">https://twitter.com/&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="closing-thoughts">
&lt;a class="heading-anchor-link" href="#closing-thoughts">Closing Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closing-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This setup focuses on one PWA feature—adding a desktop entry point.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://whatpwacando.today/" target="_blank" rel="noopener">https://whatpwacando.today/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>OpenAI Implementation for Generating Shell Commands from Descriptions</title><link>https://en.1991421.cn/2023/01/27/openaishell/</link><pubDate>Fri, 27 Jan 2023 22:48:25 +0800</pubDate><guid>https://en.1991421.cn/2023/01/27/openaishell/</guid><description>&lt;blockquote>
&lt;p>Recently researched the feasibility of using OpenAI to implement this requirement, here&amp;rsquo;s a summary&lt;/p>
&lt;/blockquote>
&lt;h2 id="competitive-product-research">
&lt;a class="heading-anchor-link" href="#competitive-product-research">Competitive Product Research&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="competitive-product-research"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>iTerm2 recently launched this feature in &lt;a href="https://iterm2.com/downloads.html" target="_blank" rel="noopener">3.5.0 beta&lt;/a>, but being a beta version, the experience is quite limited. The general implementation approach is to construct context based on user input descriptions and make requests to OpenAI. iTerm2 uses the &lt;code>text-davinci-003&lt;/code> model (GPT model). After obtaining multiple results, it displays all of them rather than just selecting the first one.&lt;/li>
&lt;li>&lt;a href="https://fig.io/user-manual/ai" target="_blank" rel="noopener">Fig&lt;/a> also introduced AI functionality, but the AI component is not directly open-sourced, so specific details are uncertain. However, through discussions in the open-source community, we know it still uses OpenAI Codex behind the scenes. In terms of user experience, after submitting a description, it only displays one result.&lt;/li>
&lt;li>&lt;a href="https://github.com/tom-doerr/zsh_codex" target="_blank" rel="noopener">zsh_codex&lt;/a> for zsh uses OpenAI and selects the &lt;code>code-davinci-002&lt;/code> model (Codex model). Taking zsh_codex implementation as an example, after obtaining multiple results, it only displays the first one.&lt;/li>
&lt;/ol>
&lt;p>From the above, we can see that all use OpenAI, with the main differences being the selected algorithm model configurations and how returned results are handled, which affects the user experience.&lt;/p>
&lt;h2 id="validation">
&lt;a class="heading-anchor-link" href="#validation">Validation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="validation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Demo can be found &lt;a href="https://github.com/alanhe421/express-demo/blob/cb91c682b05404be24d5f45fb614e8cdf2fc9661/lib/openai.js" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>Actual testing shows that using GPT/Codex to generate shell commands works as expected.&lt;/p>
&lt;p>However, there are differences in model/configuration between the two models. Here are some points to note:&lt;/p>
&lt;ol>
&lt;li>Model-Prompt&lt;/li>
&lt;/ol>
&lt;p>GPT and Codex differ in how context is constructed. Codex requires explicit language environment specification, while GPT requires clear specific requirements.&lt;/p>
&lt;ul>
&lt;li>Codex&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash\n\n# ${text}.\n
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>GPT&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">I want to write a command, the requirement is ${text}.\n
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Include periods - as shown above, periods and line breaks are necessary&lt;/li>
&lt;li>The temperature value (0-1) makes a significant difference - 0 means completely accurate, 1 means maximum possible answers (fuzzy). In the research section above, we found that iTerm2 configures it to 0, while zsh configures it to 0.5. The specific value should be determined based on the selected model and use case, this is just for reference.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>In summary, with OpenAI, generating commands from descriptions is relatively easy to implement. In practical experience, this functionality can only serve as an assistant, as it&amp;rsquo;s difficult to accurately generate a command from a single natural language description. Therefore, providing command references to users while ensuring a smooth experience is acceptable.&lt;/li>
&lt;/ol>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://help.openai.com/en/articles/5955598-is-api-usage-subject-to-any-rate-limits" target="_blank" rel="noopener">https://help.openai.com/en/articles/5955598-is-api-usage-subject-to-any-rate-limits&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Web Page Crashes Caused by File Downloads</title><link>https://en.1991421.cn/2022/12/25/file-download-crash/</link><pubDate>Sun, 25 Dec 2022 23:41:52 +0800</pubDate><guid>https://en.1991421.cn/2022/12/25/file-download-crash/</guid><description>&lt;blockquote>
&lt;p>Recently implemented an async file download feature. In Chrome, triggering downloads of files with certain sizes like 200MB causes the browser page to crash, while other browsers work fine. No errors appear in the console. The same operations work fine in Safari/Firefox - only Chrome has this issue. This problem was eventually resolved, marking it down here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error-information">
&lt;a class="heading-anchor-link" href="#error-information">Error Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Description:&lt;/p>
&lt;ul>
&lt;li>Developer Tools console shows no other error information.&lt;/li>
&lt;li>Web page error message is &lt;code>Error code:RESULT_CODE_KILLED_BAD_MESSAGE&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-12-25-234224.jpeg"
alt="https://static.1991421.cn/2022/2022-12-25-234224.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Related download logic code is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">downloadFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">packets&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">blob&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Blob&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">packets&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/octet-stream&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">downloadLink&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">appendChild&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">downloadLink&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">downloadLink&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">URL&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createObjectURL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">blob&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">downloadLink&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;_self&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">downloadLink&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;empty.txt&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">downloadLink&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">click&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Besides inconsistent behavior across different browsers, another phenomenon is that since we have two APIs for downloads, one (A) works while the other (B) doesn&amp;rsquo;t. Comparison reveals the only difference is the chunk size of binary data: one is 3KB, the other is 100KB.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since one API download method works fine, and the only difference is chunk size, the problem was directly identified: chunk size too small causes 200MB data to create an excessively large memory array, leading to page crashes. It&amp;rsquo;s understandable that other browsers don&amp;rsquo;t crash - different browsers have different limits for this.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For this issue, simply adjusting the 3KB chunk size to 100KB works. However, if file sizes increase further, this problem will still occur, so a fundamental solution is needed.&lt;/p>
&lt;p>There are 2 fundamental approaches:&lt;/p>
&lt;ol>
&lt;li>Direct streaming save - JS has Stream support, allowing streaming saves without consuming large memory, especially for large files&lt;/li>
&lt;li>Many large file downloads expose URLs and delegate to browser downloads. Here, blob means the webpage manages the download process itself, so memory overhead is unavoidable, especially problematic for large files.&lt;/li>
&lt;/ol>
&lt;h2 id="extension">
&lt;a class="heading-anchor-link" href="#extension">Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, Chrome has limited memory per tab. If a webpage uses a parameter to store 300-500MB of data, the page will also crash. Therefore, memory usage needs to be careful. For downloads, especially large data volumes, synchronous download solutions should be prioritized.&lt;/p></description></item><item><title>Handling Cancel Events for Input File Upload</title><link>https://en.1991421.cn/2022/12/07/input/</link><pubDate>Wed, 07 Dec 2022 12:15:15 +0800</pubDate><guid>https://en.1991421.cn/2022/12/07/input/</guid><description>&lt;blockquote>
&lt;p>Recently, while handling file uploads, I needed to know if the user clicked cancel. I looked into the feasibility and here&amp;rsquo;s a summary of my findings.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;file&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="javascript-doesnt-support-this-natively">
&lt;a class="heading-anchor-link" href="#javascript-doesnt-support-this-natively">JavaScript Doesn&amp;rsquo;t Support This Natively&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="javascript-doesnt-support-this-natively"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, there&amp;rsquo;s no official solution. If a user opens the file selection dialog and then clicks cancel, JavaScript doesn&amp;rsquo;t expose an event to tell us about the cancellation. Therefore, we need a HACK solution here. Of course, if you can accept adding another layer of dialog, that&amp;rsquo;s also possible - where users subjectively click a cancel button we provide.&lt;/p>
&lt;h2 id="hack-solution">
&lt;a class="heading-anchor-link" href="#hack-solution">HACK Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hack-solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s the HACK approach:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// fileEl is the inputFile element
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fileEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onchange&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">_e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">files&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fileEl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fileEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onclick&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">_e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fileEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onfocus&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">fileEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fileEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">files&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">500&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>The execution order of onchange, onclick, and onfocus is: onfocus -&amp;gt; onclick -&amp;gt; onchange&lt;/li>
&lt;li>When users click cancel on the dialog, onfocus will definitely trigger. Considering that onfocus also executes first when files are selected, and because there&amp;rsquo;s a delay in fileEl getting file information, we add a timeout and check file length to ensure the logic works correctly&lt;/li>
&lt;li>The reason for clearing the value in onclick is that if the same file is selected again, onchange won&amp;rsquo;t trigger. You can remove this if you don&amp;rsquo;t have this requirement&lt;/li>
&lt;/ol>
&lt;h2 id="official-forum">
&lt;a class="heading-anchor-link" href="#official-forum">Official Forum&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="official-forum"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There&amp;rsquo;s discussion about this requirement in the official forum, but it&amp;rsquo;s not yet supported:&lt;/p>
&lt;p>&lt;a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=25452" target="_blank" rel="noopener">https://www.w3.org/Bugs/Public/show_bug.cgi?id=25452&lt;/a>&lt;/p></description></item><item><title>Right-Click File Download in WebShell</title><link>https://en.1991421.cn/2022/11/26/webshell/</link><pubDate>Sat, 26 Nov 2022 16:58:16 +0800</pubDate><guid>https://en.1991421.cn/2022/11/26/webshell/</guid><description>&lt;blockquote>
&lt;p>WebShell recently added right-click download support. Here’s a summary of the implementation.&lt;/p>
&lt;/blockquote>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Tech stack
&lt;ul>
&lt;li>&lt;a href="https://github.com/xtermjs/xterm.js/tree/master" target="_blank" rel="noopener">xterm.js&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/theophilusx/ssh2-sftp-client" target="_blank" rel="noopener">ssh2-sftp-client&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/rcaloras/bash-preexec" target="_blank" rel="noopener">bash-preexec&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="implementation">
&lt;a class="heading-anchor-link" href="#implementation">Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Text selection&lt;/p>
&lt;ul>
&lt;li>xterm.js supports double-click word selection. If a folder name contains a space like &lt;code>hello world&lt;/code>, you’ll need to drag to select the full name.&lt;/li>
&lt;li>The shell output doesn’t directly indicate whether text is a file, directory, or the PS1 prompt. Themes can style colors, but you can’t reliably infer types from colors since themes vary.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Current Working Directory (CWD)&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Use shell integration via function hooks. After each command finishes, capture the current directory and send it to the client, storing it in hidden escape sequences so it doesn’t render in the terminal. The app can continuously extract and cache the current directory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> /usr/local/bash-precmd/bash-preexec.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">preexec&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="nb">printf&lt;/span> &lt;span class="s2">&amp;#34;\x1B]1337;PreExec;Timestamp=&lt;/span>&lt;span class="k">$(&lt;/span>date +%s&lt;span class="k">)&lt;/span>&lt;span class="s2">;\x7&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">precmd&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="nb">printf&lt;/span> &lt;span class="s2">&amp;#34;\x1B]1337;PostExec;Exit=&lt;/span>&lt;span class="nv">$?&lt;/span>&lt;span class="s2">;CurrentDir=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">;Timestamp=&lt;/span>&lt;span class="k">$(&lt;/span>date +%s&lt;span class="k">)&lt;/span>&lt;span class="s2">;\x7&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>With CWD known and text selected, basic download is straightforward.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Directory history&lt;/p>
&lt;ul>
&lt;li>If the user selects a file on the latest line, no problem. But each line can come from a different working directory, so you need to track CWD over time.&lt;/li>
&lt;li>Don’t map CWD one-to-one by xterm’s visual line numbers. For example, &lt;code>ls&lt;/code> may span multiple terminal rows. A better approach is to associate the CWD with the N rows written by a single output operation.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>File metadata&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Selected text isn’t guaranteed to be a file. You need to detect type/size/permissions, etc. &lt;code>ssh2-sftp-client&lt;/code> exposes &lt;a href="https://github.com/theophilusx/ssh2-sftp-client#sec-4-2-5" target="_blank" rel="noopener">stat&lt;/a> for this.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>stat.mode&lt;/code> is a number like &lt;code>33279&lt;/code>. Convert it to octal to get &lt;code>rwx&lt;/code> bits:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nx">rslt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mode&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nb">parseInt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;777&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>For symlinks, &lt;code>list&lt;/code> returns &lt;code>type = 'l'&lt;/code>, but &lt;code>stat.isSymbolicLink&lt;/code> may be &lt;code>false&lt;/code>—a discrepancy without a great workaround yet.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Download&lt;/p>
&lt;ul>
&lt;li>Once you’ve validated the target, there are multiple ways to download. With &lt;code>ssh2-sftp-client&lt;/code>, use &lt;a href="https://github.com/theophilusx/ssh2-sftp-client#getpath-dst-options--stringstreambuffer" target="_blank" rel="noopener">get&lt;/a> or &lt;a href="https://github.com/theophilusx/ssh2-sftp-client#fastgetremotepath-localpath-options--string" target="_blank" rel="noopener">fastGet&lt;/a>. If you’re in an SSH session, sz also works.&lt;/li>
&lt;li>Empty files are supported by these methods. Note that with &lt;code>get&lt;/code> as a stream, the &lt;code>data&lt;/code> event may not fire on empty files, but &lt;code>end&lt;/code> will.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With right-click download, the experience is smoother, though the underlying mechanism is similar to other download paths.&lt;/p></description></item><item><title>Cannot use import statement outside a module</title><link>https://en.1991421.cn/2022/10/29/d85a203/</link><pubDate>Sat, 29 Oct 2022 21:12:40 +0800</pubDate><guid>https://en.1991421.cn/2022/10/29/d85a203/</guid><description>&lt;blockquote>
&lt;p>Recently, while building a Node project in Docker locally, I got this error: &lt;code>Cannot use import statement outside a module&amp;quot;,&amp;quot;event&amp;quot;:&amp;quot;uncaughtException&amp;quot;&lt;/code>. After troubleshooting, I noted the fix here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="environment">
&lt;a class="heading-anchor-link" href="#environment">Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>NodeJS version &lt;code>v12&lt;/code>&lt;/li>
&lt;li>The project uses the &lt;code>CommonJS&lt;/code> standard&lt;/li>
&lt;/ul>
&lt;h2 id="meaning-of-the-error">
&lt;a class="heading-anchor-link" href="#meaning-of-the-error">Meaning of the error&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="meaning-of-the-error"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The error stack pointed to &lt;code>node_modules/axios/lib/core/buildFullPath.js&lt;/code>. In local development the file looked fine, but inside the Docker container it used ES import syntax. That confirmed the root cause.&lt;/p>
&lt;h2 id="root-cause">
&lt;a class="heading-anchor-link" href="#root-cause">Root cause&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="root-cause"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>During Docker builds, package.lock and Node versions should be fixed, so it should match the dev environment. The mismatch implies Docker build caching issues.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>History showed I previously installed axios@latest (v1) because I did not pin a version. The v1 package is ES module based. So the likely cause is Docker using a cached build layer. I found &lt;code>docker builder prune&lt;/code> can clear build cache. After pruning and rebuilding, it worked.&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>SSH Login Failure — Connection reset by peer</title><link>https://en.1991421.cn/2022/10/20/ssh-kex-exchange-identification/</link><pubDate>Thu, 20 Oct 2022 20:35:52 +0800</pubDate><guid>https://en.1991421.cn/2022/10/20/ssh-kex-exchange-identification/</guid><description>&lt;blockquote>
&lt;p>If SSH login fails with &lt;code>kex_exchange_identification: read: Connection reset by peer&lt;/code>, try the following checks.&lt;/p>
&lt;/blockquote>
&lt;p>When using &lt;code>ssh&lt;/code>, the &lt;code>-v&lt;/code> flag prints debug logs. In the logs below, the server never sends anything; if the first step succeeded, you would see the remote protocol version.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-10-20-203719.jpeg"
alt="https://static.1991421.cn/2022/2022-10-20-203719.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Likely places to check:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;code>/etc/ssh/sshd_config&lt;/code>&lt;/p>
&lt;p>Verify the port is correct; it may not be 22. If you change the config, restart the service with &lt;code>service sshd restart&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>/etc/hosts.allow&lt;/code> and &lt;code>/etc/hosts.deny&lt;/code>&lt;/p>
&lt;p>By default these files are empty with no active rules; make sure nothing is blocking your client.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Note: editing the above files requires &lt;code>sudo&lt;/code> or root privileges.&lt;/p></description></item><item><title>SSH Login to GitHub Failed</title><link>https://en.1991421.cn/2022/10/02/sshgithub/</link><pubDate>Sun, 02 Oct 2022 17:11:34 +0800</pubDate><guid>https://en.1991421.cn/2022/10/02/sshgithub/</guid><description>&lt;blockquote>
&lt;p>While staying in Hubei recently, I ran into an issue accessing GitHub — SSH login kept failing even though my public key was configured correctly. It turned out to be DNS pollution. Here are the notes and fix.&lt;/p>
&lt;/blockquote>
&lt;h2 id="network-environment">
&lt;a class="heading-anchor-link" href="#network-environment">Network Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="network-environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>China Telecom, Xianning, Hubei&lt;/li>
&lt;/ul>
&lt;h2 id="test-script">
&lt;a class="heading-anchor-link" href="#test-script">Test Script&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="test-script"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If git operations prompt for a password for &lt;code>git@github.com&lt;/code>, the SSH login failed. Use the following to test SSH login status:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ssh -vT git@github.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The logs showed public key verification failing. First suspicion: the key wasn’t configured correctly on GitHub. After confirming it was correct and the failure persisted, DNS pollution became the likely culprit.&lt;/p>
&lt;p>Switching the terminal to use a proxy made the login succeed, confirming the diagnosis.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using &lt;code>dig&lt;/code> with the default DNS server vs Google DNS (8.8.8.8) showed the default DNS returned &lt;code>223.75.236.241&lt;/code>, which is not a GitHub web address. The correct IP was &lt;code>20.205.243.166&lt;/code>; visiting it in a browser redirected correctly to GitHub.&lt;/p>
&lt;img src="https://static.1991421.cn/2022/2022-10-02-233445.png" alt="https://static.1991421.cn/2022/2022-10-02-233445.png" style="zoom:67%;" />
&lt;h2 id="workaround">
&lt;a class="heading-anchor-link" href="#workaround">Workaround&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="workaround"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since this was a temporary issue, I modified the hosts file and added:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">20.205.243.166 github.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Alternatively, configure your terminal to use a proxy.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Accessing GitHub/Google cleanly can be tricky at times — consider it practice in troubleshooting.&lt;/p></description></item><item><title>About npm publish: Something you may not know.</title><link>https://en.1991421.cn/2022/09/24/npm-publish/</link><pubDate>Sat, 24 Sep 2022 16:43:36 +0800</pubDate><guid>https://en.1991421.cn/2022/09/24/npm-publish/</guid><description>&lt;blockquote>
&lt;p>The npm publish command is used to publish a JS package to a private or public registry. However, I previously ignored how the lock file is handled during publishing, such as the processing of the &lt;code>resolutions&lt;/code> field. Here, I will organize the issues I have learned.&lt;/p>
&lt;/blockquote>
&lt;h2 id="package-publish-without-package-lockjson">
&lt;a class="heading-anchor-link" href="#package-publish-without-package-lockjson">Package publish without package-lock.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-publish-without-package-lockjson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>When developing a JS package, we need to include &lt;code>package-lock.json&lt;/code> in VCS management, but the lock file will not be published when using &lt;code>npm publish&lt;/code>.&lt;/li>
&lt;li>When we execute the npm install command in a specific project, the lock file of the top-level package (i.e., the target project) zeds for a recognize-specific version installation. Still, the lock files included in the dependent packages will be directly ignored.&lt;/li>
&lt;/ol>
&lt;h3 id="including-lock-file-in-published-package">
&lt;a class="heading-anchor-link" href="#including-lock-file-in-published-package">Including lock file in published package?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="including-lock-file-in-published-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>With npm cli &amp;gt;=v6, &lt;code>package-lock.json&lt;/code> will not be published regardless of whether it is configured in the &lt;code>package.json&lt;/code> files whitelist. However, under the older CLI, it can be published through a whitelist configuration.&lt;/li>
&lt;li>Testing with v6 will show this issue. From the official version history, it can be seen that, for example, nodev8 default npm CLI is v6, and trying that version can reproduce this issue.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-24-235551.jpeg"
alt="https://static.1991421.cn/2022/2022-09-24-235551.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="preinstall-execution-with-resolutions-not-working">
&lt;a class="heading-anchor-link" href="#preinstall-execution-with-resolutions-not-working">&lt;code>preinstall&lt;/code> execution with &lt;code>resolutions&lt;/code> not working&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="preinstall-execution-with-resolutions-not-working"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes, resolutions control the versions of indirect dependencies and are configured under the &lt;code>preinstall&lt;/code> hook for execution. However, as mentioned above, the published package does not include the lock file, so this hook needs to be removed; otherwise, &lt;code>npm install&lt;/code> the actual project will result in an error of not finding the lock file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;preinstall&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx npm-force-resolutions&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="extracting-the-packaged-content-with-the-npm-pack">
&lt;a class="heading-anchor-link" href="#extracting-the-packaged-content-with-the-npm-pack">Extracting the packaged content with the npm pack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extracting-the-packaged-content-with-the-npm-pack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes, you can execute npm pack in the project directory to extract the published NPM package. If you want to use the development package directly for debugging in a specific project, use &lt;code>npm link&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-24-170314.jpg"
alt="https://static.1991421.cn/2022/2022-09-24-170314.jpg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>Google Cloud 3-Month Free Trial</title><link>https://en.1991421.cn/2022/09/08/google-cloud3/</link><pubDate>Thu, 08 Sep 2022 23:47:36 +0800</pubDate><guid>https://en.1991421.cn/2022/09/08/google-cloud3/</guid><description>&lt;blockquote>
&lt;p>Google Cloud offers a &lt;a href="https://cloud.google.com/free/docs/free-cloud-features?hl=zh-cn" target="_blank" rel="noopener">free plan&lt;/a> where you can receive &lt;code>$300&lt;/code> in credits and a &lt;code>90-day&lt;/code> free trial. I tried it out for work purposes, so I&amp;rsquo;m documenting the process here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="application-requirements">
&lt;a class="heading-anchor-link" href="#application-requirements">Application Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="application-requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Visa card&lt;/li>
&lt;li>Google account&lt;/li>
&lt;li>Internet access that supports external connections&lt;/li>
&lt;/ol>
&lt;h2 id="application-steps">
&lt;a class="heading-anchor-link" href="#application-steps">Application Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="application-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Directly visit &lt;a href="https://console.cloud.google.com" target="_blank" rel="noopener">https://console.cloud.google.com&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If not logged into Google account, log in. After logging in, directly &lt;code>click Try&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Fill in information, region/Visa card&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Enter the console&lt;/p>
&lt;ul>
&lt;li>
&lt;p>First click &lt;code>Billing - Overview&lt;/code> to see the credited $300 and trial days. Mine shows 2K+ because it&amp;rsquo;s in HK currency&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-08-234152.jpeg"
alt="https://static.1991421.cn/2022/2022-09-08-234152.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Be careful not to click the activation button on the top right. &lt;em>If activated, after the free trial period ends, the system will automatically start billing&lt;/em>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-08-232511.jpeg"
alt="https://static.1991421.cn/2022/2022-09-08-232511.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>With the credits, you can purchase machines. Click the left menu - select Virtual Machine Instances - Create Instance - choose as needed&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-08-233245.jpeg"
alt="https://static.1991421.cn/2022/2022-09-08-233245.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Besides virtual machine instances, other services can also be purchased. Pay attention to the bottom prompt information - as long as it supports using credits for purchase.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-08-233456.jpeg"
alt="https://static.1991421.cn/2022/2022-09-08-233456.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="6">
&lt;li>After creating an instance, if you want to use local terminal login, you need to manually edit the instance and add a public key. The local SSH login username corresponds to the public key username, which can be viewed after adding.&lt;/li>
&lt;/ol>
&lt;p>At this point, you&amp;rsquo;ve obtained a Google Cloud machine.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>I launched a Hong Kong virtual machine here, connected via Surge SSH, and tested the speed - it&amp;rsquo;s decent.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-08-234345.jpeg"
alt="https://static.1991421.cn/2022/2022-09-08-234345.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="2">
&lt;li>According to Google, if not activated after expiration, it won&amp;rsquo;t automatically renew, so just focus on using the credits well. When it expires, just leave it be.&lt;/li>
&lt;/ol></description></item><item><title>FreeBSD Password Login Timeout Error</title><link>https://en.1991421.cn/2022/08/21/freebsdtimeout/</link><pubDate>Sun, 21 Aug 2022 19:33:31 +0800</pubDate><guid>https://en.1991421.cn/2022/08/21/freebsdtimeout/</guid><description>&lt;blockquote>
&lt;p>When the online WebShell executes password login to FreeBSD 13.0 64-bit, it reports a timeout error, which prompted me to start investigating. Here&amp;rsquo;s the analysis:&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-08-21-130943.jpeg"
alt="https://static.1991421.cn/2022/2022-08-21-130943.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-08-21-191440.jpeg"
alt="https://static.1991421.cn/2022/2022-08-21-191440.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="troubleshooting">
&lt;a class="heading-anchor-link" href="#troubleshooting">Troubleshooting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="troubleshooting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>The direct cause of the error is SSH login timeout. After examining the SSH2 module and its specific call logic, I found two possibilities that could cause timeout:&lt;/p>
&lt;ol>
&lt;li>When executing keyboard-interactive login, if the user never provides input, it will timeout. If the network is disconnected, the error would be &lt;code>ECONNRESET&lt;/code>&lt;/li>
&lt;li>If the SSH handshake server never responds, it will trigger a timeout&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>According to the current client login logic, although the client tries multiple login methods in rotation, keyboard-interactive login is enabled in the current logic, but it can only be triggered when public key login fails&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Testing in an environment consistent with the online program reproduces the issue, and the production environment consistently reproduces it. Local terminals like iTerm2 direct login do not reproduce this problem.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Trying to use the ssh command directly on the production server&lt;/p>
&lt;p>Specific timing tests revealed that when testing direct command-line ssh connections on the production server, after &lt;code>20s+&lt;/code>, it reports &lt;code>ssh_exchange_identification: read: Connection reset by peer&lt;/code>. Comparing this with the default timeout mentioned above, this makes sense.&lt;/p>
&lt;!-- more -->
&lt;ol>
&lt;li>Production Environment&lt;/li>
&lt;/ol>
&lt;img src="https://static.1991421.cn/2022/2022-08-21-221211.jpeg" alt="https://static.1991421.cn/2022/2022-08-21-221211.jpeg" />
&lt;ol start="2">
&lt;li>Test Environment&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-08-21-221228.jpeg"
alt="https://static.1991421.cn/2022/2022-08-21-221228.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Based on this, it&amp;rsquo;s basically confirmed that it&amp;rsquo;s related to environment differences&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>After understanding the current production and test environments, one obvious difference is that the test environment machines have direct internet access, while production does not. Using this obvious difference as an entry point, I finally consulted system/network experts and figured out the problem:&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>Packet capturing of the SSH handshake process in production/test environments revealed that in the production environment, ACK packets during TCP handshake do not carry timestamps, while the pre-production environment does&lt;/p>
&lt;/li>
&lt;li>
&lt;p>FreeBSD is strictly designed according to the RFC1323 PAWS mechanism. When both SYN and SYN+ACK steps contain timestamp options, the PAWS mechanism is enabled. If the third ACK packet doesn&amp;rsquo;t contain a timestamp, it&amp;rsquo;s directly dropped&lt;/p>
&lt;/li>
&lt;li>
&lt;p>FreeBSD systems have timestamp checking enabled by default, while other systems like Ubuntu/CentOS don&amp;rsquo;t have it enabled, or don&amp;rsquo;t have this setting&lt;/p>
&lt;p>You can view related configurations using the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo sysctl -a | grep net|grep rfc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>In FreeBSD 12.3, &lt;code>net.inet.tcp.tolerate_missing_ts&lt;/code> defaults to 1, while in version 13 it defaults to 0&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>FreeBSD-side modification&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Temporary modification&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sysctl&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">w&lt;/span> &lt;span class="n">net&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">inet&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tcp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tolerate_missing_ts&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Permanent modification&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sysctl&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">conf&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">net&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">inet&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tcp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tolerate_missing_ts&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">rc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">sysctl&lt;/span> &lt;span class="n">reload&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This setting ignores timestamp validation after configuration&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Client-side modification (current WebShell client side)&lt;/p>
&lt;p>&lt;code>net.ipv4.tcp_wan_timestamps=1&lt;/code>, and also ensure &lt;code>net.ipv4.tcp_timestamps=1&lt;/code>&lt;/p>
&lt;p>This carries timestamps even on non-public networks&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Ultimately, we chose solution 1. Solution 2 was rejected because the current service is deployed in containers and doesn&amp;rsquo;t have this setting. It would require modifying the host machine and changing network configurations, which poses high risks.&lt;/p>
&lt;p>Testing the above solution confirmed that the problem was resolved.&lt;/p>
&lt;h2 id="supplement">
&lt;a class="heading-anchor-link" href="#supplement">Supplement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supplement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="command-line-scripts">
&lt;a class="heading-anchor-link" href="#command-line-scripts">Command Line Scripts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="command-line-scripts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The terminal SSH connections mentioned above can be used to debug the login authentication process. Here are the scripts:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">ssh -p &lt;span class="m">22&lt;/span> -v root@ip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh -p &lt;span class="m">22&lt;/span> -v -i privateKey root@ip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="packet-capture-tools">
&lt;a class="heading-anchor-link" href="#packet-capture-tools">Packet Capture Tools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packet-capture-tools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>tcpdump&lt;/li>
&lt;li>Analysis tool: wireshark&lt;/li>
&lt;/ol>
&lt;h3 id="ssh-server-login-authentication-configuration">
&lt;a class="heading-anchor-link" href="#ssh-server-login-authentication-configuration">SSH Server Login Authentication Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ssh-server-login-authentication-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Whether keyboard-interactive login will be attempted depends not only on client control but also on whether the server has it enabled. Different systems have different default support for login methods. Ubuntu/CentOS systems have keyboard-interactive disabled by default, while FreeBSD has it enabled.&lt;/p>
&lt;p>Configuration documentation: &lt;a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config" target="_blank" rel="noopener">https://www.freebsd.org/cgi/man.cgi?query=ssh_config&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">ChallengeResponseAuthentication yes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The problem is now resolved. In addition to the SSH handshake failing due to timestamp issues, it&amp;rsquo;s also important to note that SSH login itself will attempt multiple authentication methods.&lt;/p>
&lt;h2 id="related-issues">
&lt;a class="heading-anchor-link" href="#related-issues">Related Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=250499" target="_blank" rel="noopener">https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=250499&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/mscdex/ssh2/issues/393" target="_blank" rel="noopener">https://github.com/mscdex/ssh2/issues/393&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/moooofly/MarkSomethingDown/blob/master/Linux/%E5%85%B3%E4%BA%8E%20TCP%20%E5%8D%8F%E8%AE%AE%E4%B8%AD%E7%9A%84%20timestamp%20%E9%97%AE%E9%A2%98.md" target="_blank" rel="noopener">On TCP timestamp issues&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How whoer Detects Your DNS Resolver</title><link>https://en.1991421.cn/2022/08/07/whoerdns/</link><pubDate>Sun, 07 Aug 2022 16:46:14 +0800</pubDate><guid>https://en.1991421.cn/2022/08/07/whoerdns/</guid><description>&lt;blockquote>
&lt;p>While registering for Spotify I came across whoer and noticed it can detect your DNS server. Curious how that works, I looked into it—here’s the gist.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-08-07-164940.jpeg"
alt="https://static.1991421.cn/2022/2022-08-07-164940.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;!-- more -->
&lt;ol>
&lt;li>
&lt;p>The whoer page triggers a CSS request like &lt;code>https://sdvtq1659862.ed.whrq.net/css/null.css?_=1659862263203&lt;/code>.&lt;/p>
&lt;p>The subdomain is randomized and the file doesn’t exist.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Since the DNS answer won’t be cached locally, the resolver walks up the hierarchy and ultimately queries the authoritative DNS for &lt;code>whrq.net&lt;/code>. That DNS server records the randomized subdomain and the source DNS IP for the query.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>whoer’s backend correlates the recorded subdomain (e.g., &lt;code>sdvtq1659862&lt;/code>) with the user’s session. The client then issues a request containing that token, and the server returns the detected DNS info.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-08-07-165455.jpeg"
alt="https://static.1991421.cn/2022/2022-08-07-165455.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>In short, whoer leverages the DNS resolution path to learn your DNS server’s address. Because DNS queries only include the domain, randomized subdomains provide per-user correlation.&lt;/p>
&lt;p>Other fields are obtained similarly: OS/browser from the User-Agent, ISP from your IP address.&lt;/p>
&lt;p>Done—that’s the core idea.&lt;/p>
&lt;h2 id="related-sites">
&lt;a class="heading-anchor-link" href="#related-sites">Related Sites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-sites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://whoer.net/zh" target="_blank" rel="noopener">https://whoer.net/zh&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Hotkey Planning in WebShell</title><link>https://en.1991421.cn/2022/08/01/webshell/</link><pubDate>Mon, 01 Aug 2022 23:02:16 +0800</pubDate><guid>https://en.1991421.cn/2022/08/01/webshell/</guid><description>&lt;blockquote>
&lt;p>The WebShell I&amp;rsquo;m working on recently needs to support hotkeys. Here&amp;rsquo;s a summary of the overall approach.&lt;/p>
&lt;/blockquote>
&lt;h2 id="hotkey-philosophy">
&lt;a class="heading-anchor-link" href="#hotkey-philosophy">Hotkey Philosophy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hotkey-philosophy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, clarify the design principles:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Hotkey combinations need to be semantic &lt;code>for easy user understanding and memorization&lt;/code>, not counter-intuitive, while being easy for users to operate&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Hotkeys should avoid commonly used hotkeys, such as browser hotkeys. Even if they can be intercepted and overridden, this should be considered carefully&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="hotkey-conflicts">
&lt;a class="heading-anchor-link" href="#hotkey-conflicts">Hotkey Conflicts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hotkey-conflicts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Web pages are limited by system/browser/browser plugin hotkey conflicts. Since this is a web terminal, terminals themselves have some hotkeys/keys, so we need to consider hotkeys from several aspects.&lt;/p>
&lt;h3 id="terminal-native-hotkeys">
&lt;a class="heading-anchor-link" href="#terminal-native-hotkeys">Terminal Native Hotkeys&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="terminal-native-hotkeys"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>⌥ ⬅️ Move cursor left to the beginning of the previous word, no corresponding support on Windows&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⌥ ➡️ Move cursor left to the beginning of the next word, no corresponding support on Windows&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⌘ ⬅️ Move cursor to beginning of line, Home on Windows&lt;/p>
&lt;/li>
&lt;li>
&lt;p>⌘ ➡️ Move cursor to end of line, End on Windows&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Control C Cancel&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="browser-level-hotkeys">
&lt;a class="heading-anchor-link" href="#browser-level-hotkeys">Browser-Level Hotkeys&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-level-hotkeys"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Browsers already have some hotkeys, and some can be intercepted while others cannot.&lt;/p>
&lt;p>After actual testing on Mac Chrome, here are the results:&lt;/p>
&lt;h3 id="interceptable-hotkeys">
&lt;a class="heading-anchor-link" href="#interceptable-hotkeys">Interceptable Hotkeys&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="interceptable-hotkeys"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>⌘ F&lt;/code>&lt;/li>
&lt;li>&lt;code>⌘ L&lt;/code>&lt;/li>
&lt;li>&lt;code>⌘ P&lt;/code>&lt;/li>
&lt;li>&lt;code>⌘ ,&lt;/code>&lt;/li>
&lt;li>&lt;code>⌘ S&lt;/code>&lt;/li>
&lt;li>&lt;code>ESC&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="non-interceptable-hotkeys">
&lt;a class="heading-anchor-link" href="#non-interceptable-hotkeys">Non-interceptable Hotkeys&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="non-interceptable-hotkeys"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>⌘ N&lt;/code> Create new window&lt;/li>
&lt;li>&lt;code>⌘ T&lt;/code> Create new Tab&lt;/li>
&lt;li>&lt;code>⌘ W&lt;/code> Close Tab&lt;/li>
&lt;li>&lt;code>⌘ ⇧ [ &lt;/code> Left Tab&lt;/li>
&lt;li>&lt;code>⌘ ⇧ [ &lt;/code> Right Tab&lt;/li>
&lt;/ul>
&lt;h3 id="system-hotkeys">
&lt;a class="heading-anchor-link" href="#system-hotkeys">System Hotkeys&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="system-hotkeys"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>⌘ Tab&lt;/code> App switching&lt;/li>
&lt;/ul>
&lt;h2 id="final-solution">
&lt;a class="heading-anchor-link" href="#final-solution">Final Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on the above situation, the following solution is designed:&lt;/p>
&lt;p>Extract the following concepts:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Action, such as &lt;code>term:clearScreen&lt;/code> for clearing screen. The &amp;ldquo;term&amp;rdquo; part indicates scope, and &amp;ldquo;clearScreen&amp;rdquo; part indicates the function is screen clearing&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HotKey, such as &lt;code>command+k&lt;/code> for a specific hotkey&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Actions can bind multiple hotkeys, and the same hotkey combination can also be bound to multiple Actions.&lt;/p>
&lt;h3 id="processing-logic">
&lt;a class="heading-anchor-link" href="#processing-logic">Processing Logic&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="processing-logic"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Globally capture and listen to all hotkeys under N Actions&lt;/p>
&lt;ol>
&lt;li>When a hotkey is triggered, determine whether it&amp;rsquo;s an event from the target Scope based on the element that triggered the event and the Action bound during binding. If not, ignore the event. If yes, trigger the corresponding action behavior and mark the event as caught. This facilitates subsequent bubbling event processing.&lt;/li>
&lt;/ol>
&lt;h2 id="hotkey-planning">
&lt;a class="heading-anchor-link" href="#hotkey-planning">Hotkey Planning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hotkey-planning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here are some examples for reference:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Function&lt;/th>
&lt;th style="text-align: left">Mac&lt;/th>
&lt;th style="text-align: left">Windows&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Hotkey Help&lt;/td>
&lt;td style="text-align: left">⌘ /&lt;/td>
&lt;td style="text-align: left">Ctrl /&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Settings&lt;/td>
&lt;td style="text-align: left">⌘ ,&lt;/td>
&lt;td style="text-align: left">Ctrl ,&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Tab Switch&lt;/td>
&lt;td style="text-align: left">⌃ 1-5&lt;/td>
&lt;td style="text-align: left">Ctrl 1-5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">New Tab&lt;/td>
&lt;td style="text-align: left">⌥ T&lt;/td>
&lt;td style="text-align: left">Alt T&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Close Tab&lt;/td>
&lt;td style="text-align: left">⌥ W&lt;/td>
&lt;td style="text-align: left">Alt W&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Search&lt;/td>
&lt;td style="text-align: left">⌘ F&lt;/td>
&lt;td style="text-align: left">Ctrl F&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Increase Font&lt;/td>
&lt;td style="text-align: left">⌘ ⇧ +&lt;/td>
&lt;td style="text-align: left">Ctrl +&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Decrease Font&lt;/td>
&lt;td style="text-align: left">⌘ ⇧ -&lt;/td>
&lt;td style="text-align: left">Ctrl -&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">AI Command&lt;/td>
&lt;td style="text-align: left">⌃ `&lt;/td>
&lt;td style="text-align: left">Ctrl `&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Command Palette&lt;/td>
&lt;td style="text-align: left">⌘⇧P&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="extensions">
&lt;a class="heading-anchor-link" href="#extensions">Extensions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extensions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="apple-platform-detection">
&lt;a class="heading-anchor-link" href="#apple-platform-detection">Apple Platform Detection&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="apple-platform-detection"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Different platforms have different hotkey bindings, so platform detection is needed.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sr">/Mac|iPod|iPhone|iPad/&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">platform&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mod-key">
&lt;a class="heading-anchor-link" href="#mod-key">Mod Key&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mod-key"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In hotkey planning, the &lt;code>⌘&lt;/code> key on Mac corresponds to the &lt;code>⌃&lt;/code> key on Windows, not the Windows key. This needs attention.&lt;/p>
&lt;h2 id="windows-ctrl-leftright">
&lt;a class="heading-anchor-link" href="#windows-ctrl-leftright">Windows Ctrl Left/Right&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="windows-ctrl-leftright"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Hyper has &lt;a href="https://github.com/vercel/hyper/issues/2873" target="_blank" rel="noopener">disabled this support&lt;/a>, but my personal testing shows it works.&lt;/p>
&lt;h2 id="full-hotkey-support">
&lt;a class="heading-anchor-link" href="#full-hotkey-support">Full Hotkey Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="full-hotkey-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned above, many hotkeys on web pages cannot be captured and processed because they are directly supported by browsers. However, this pain point can be solved by using &lt;code>keyboard.lock&lt;/code> to lock certain keys while the webpage is in PWA/fullscreen mode.&lt;/p>
&lt;ol>
&lt;li>Actual testing found that P/N keys cannot be locked successfully.&lt;/li>
&lt;li>The keyboard.lock API has limited compatibility - currently Chrome is OK, Safari/Firefox don&amp;rsquo;t support it yet.&lt;/li>
&lt;/ol></description></item><item><title>Alternate Screen Explained (Beginner's Guide)</title><link>https://en.1991421.cn/2022/07/28/alternate-screen/</link><pubDate>Thu, 28 Jul 2022 22:42:13 +0800</pubDate><guid>https://en.1991421.cn/2022/07/28/alternate-screen/</guid><description>&lt;blockquote>
&lt;p>When implementing terminal search highlighting using xterm.js, it&amp;rsquo;s necessary to recognize whether the terminal is currently in vi editor mode. Upon investigation, it was found that &lt;a href="https://github.com/xtermjs/xterm.js/issues/2694" target="_blank" rel="noopener">xterm.buffer.active.type&lt;/a> can be used for this purpose.&lt;/p>
&lt;p>Upon examining the corresponding type definitions, it was found that the values are only &amp;ldquo;normal&amp;rdquo; and &amp;ldquo;alternate.&amp;rdquo; It wasn&amp;rsquo;t clear why it&amp;rsquo;s called &amp;ldquo;alternate,&amp;rdquo; so further investigation revealed that this concept originates from the terminal&amp;rsquo;s alternate screen. Here&amp;rsquo;s an overall introduction.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-07-28-224538.jpeg"
alt="https://static.1991421.cn/2022/2022-07-28-224538.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="alternate-screen">
&lt;a class="heading-anchor-link" href="#alternate-screen">Alternate Screen&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="alternate-screen"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The alternate screen is a temporary screen for interactive input, which eventually returns to the main screen. For example, in VI mode, the screen display area is the same as in normal mode. When the alternate screen is activated, the original screen content is saved, and the original content is restored upon exiting the alternate screen.&lt;/li>
&lt;li>Therefore, the normal/alternate concepts in xterm are not original but are inherent to the terminal&amp;rsquo;s design, with these two modes being standard and alternate.&lt;/li>
&lt;/ol>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>xterm.js is just one implementation of xterm. To better understand xterm.js, learning more about the design of xterm—the terminal emulator is necessary.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://support.apple.com/zh-cn/guide/terminal/trmld1f46097/mac" target="_blank" rel="noopener">Display or hide the alternate screen in Terminal on Mac&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://superuser.com/questions/321204/what-does-terminal-apps-show-alternate-screen-do-os-x" target="_blank" rel="noopener">What does Terminal app&amp;rsquo;s &amp;ldquo;Show Alternate Screen&amp;rdquo; do? (OS X)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://linux.die.net/man/1/xterm" target="_blank" rel="noopener">xterm - Linux man page&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Makefile Explained (Simple Guide)</title><link>https://en.1991421.cn/2022/07/24/makefile/</link><pubDate>Sun, 24 Jul 2022 15:48:11 +0800</pubDate><guid>https://en.1991421.cn/2022/07/24/makefile/</guid><description>&lt;blockquote>
&lt;p>While examining projects, I noticed that some projects, especially backend projects, have a Makefile file, which allows convenient encapsulation and calling of build commands.&lt;/p>
&lt;p>I hadn&amp;rsquo;t learned about this tool before, so I&amp;rsquo;m documenting it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="features">
&lt;a class="heading-anchor-link" href="#features">Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Native support on Mac/Linux - once declared and created, you can execute it directly
&lt;ul>
&lt;li>For shell files, you still need to manually add execute permissions &lt;code>chmod +x *.sh&lt;/code>&lt;/li>
&lt;li>Using Node.js to implement build tools in projects is also possible, but there are still environmental dependencies&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Supports variable parameter passing&lt;/li>
&lt;/ol>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-makefile" data-lang="makefile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Build frontend image make build version=1.0.0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nf">build&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> docker build -t stacker/web:v&lt;span class="k">$(&lt;/span>version&lt;span class="k">)&lt;/span> .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For example, encapsulating the above command into a &lt;code>Makefile&lt;/code> allows you to simply run &lt;code>make build version=1.0.0&lt;/code> whenever you want to package a new image file&lt;/p>
&lt;h3 id="syntax-notes">
&lt;a class="heading-anchor-link" href="#syntax-notes">Syntax Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="syntax-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Recommended filename is &lt;code>Makefile&lt;/code>, but &lt;code>makefile&lt;/code> is also acceptable&lt;/li>
&lt;li>Only supports single-line comments&lt;/li>
&lt;li>Commands starting with @ indicate that the command output should not be printed&lt;/li>
&lt;/ul>
&lt;h2 id="common-errors">
&lt;a class="heading-anchor-link" href="#common-errors">Common Errors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="makefile5--missing-separator--stop">
&lt;a class="heading-anchor-link" href="#makefile5--missing-separator--stop">Makefile:5: *** missing separator. Stop.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="makefile5--missing-separator--stop"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Commands must be indented with tabs, not spaces&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-11-29-000144.jpeg"
alt="https://static.1991421.cn/2022/2022-11-29-000144.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="reference-configurations">
&lt;a class="heading-anchor-link" href="#reference-configurations">Reference Configurations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-configurations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://gitlab.com/gnachman/iterm2/-/blob/master/Makefile" target="_blank" rel="noopener">https://gitlab.com/gnachman/iterm2/-/blob/master/Makefile&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jcsalterego/pngpaste/blob/master/Makefile" target="_blank" rel="noopener">https://github.com/jcsalterego/pngpaste/blob/master/Makefile&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Ubuntu 22.04 SSH-RSA Login Failure</title><link>https://en.1991421.cn/2022/07/18/ubuntu-22-04-ssh-the-rsa-key-isn/</link><pubDate>Mon, 18 Jul 2022 22:00:10 +0800</pubDate><guid>https://en.1991421.cn/2022/07/18/ubuntu-22-04-ssh-the-rsa-key-isn/</guid><description>&lt;blockquote>
&lt;p>Users reported that our WebShell couldn’t log into Ubuntu 22.04 via SSH key—authentication kept failing. Here’s the investigation and fix.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-07-18-215907.jpeg"
alt="error screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;code>ssh2&lt;/code> (the Node.js library we use) reported:&lt;/p>
&lt;blockquote>
&lt;p>Client: publickey auth failed&lt;br>
Error: All configured authentication methods failed&lt;/p>
&lt;/blockquote>
&lt;h2 id="investigation">
&lt;a class="heading-anchor-link" href="#investigation">Investigation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="investigation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Keys were RSA.&lt;/li>
&lt;li>Logging in via Terminal/iTerm2 on macOS worked:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ssh -v -i ~/test.pem ubuntu@1.221.54.8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>So our WebShell implementation (ssh2) was the outlier.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Tip:&lt;/strong> If you see &lt;code>Permissions 0644 for 'test.pem' are too open&lt;/code>, run &lt;code>chmod 400 test.pem&lt;/code>. Type &lt;code>exit&lt;/code> to close the session.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Both Terminal and iTerm2 succeed; apps built on the &lt;code>ssh2&lt;/code> module (Tabby, Electerm, etc.) fail. So the issue is in ssh2.&lt;/li>
&lt;li>Ubuntu 22.04 disables &lt;code>ssh-rsa&lt;/code> signatures by default; older releases didn’t.&lt;/li>
&lt;/ul>
&lt;p>Conclusion: new OpenSSH servers reject SHA-1 signatures, while ssh2 still tries to use them. Local terminals handle the fallback; ssh2 doesn’t (yet).&lt;/p>
&lt;h2 id="fixes">
&lt;a class="heading-anchor-link" href="#fixes">Fixes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fixes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Server configuration&lt;/strong> — allow &lt;code>ssh-rsa&lt;/code> temporarily:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo vi /etc/ssh/sshd_config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PubkeyAcceptedKeyTypes +ssh-rsa
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo service sshd restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>Regenerate keys&lt;/strong> with stronger algorithms (e.g., &lt;code>ed25519&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Upgrade ssh2&lt;/strong> — use a commit/PR that adds &lt;code>rsa-sha2-512/256&lt;/code> support:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;ssh2&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;git@github.com:Eugeny/ssh2.git#22735cecf1d9c118b2b8af1c2f80fe5b04996fe1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>All three solutions work.&lt;/p>
&lt;h2 id="why-ssh-rsa-is-deprecated">
&lt;a class="heading-anchor-link" href="#why-ssh-rsa-is-deprecated">Why ssh-rsa Is Deprecated&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-ssh-rsa-is-deprecated"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>From OpenSSH 8.2 release notes:&lt;/p>
&lt;blockquote>
&lt;p>Chosen-prefix attacks against SHA-1 now cost &amp;lt; $50K. We’ll disable the &lt;code>ssh-rsa&lt;/code> signature algorithm by default in a future release.&lt;/p>
&lt;/blockquote>
&lt;p>RSA itself is fine; SHA-1 is weak. Ubuntu 22.04 prefers &lt;code>rsa-sha2-512/256&lt;/code>. ssh2 failed because it kept signing with SHA-1. Update: &lt;a href="https://github.com/Eugeny/ssh2/commit/22735cec" target="_blank" rel="noopener">https://github.com/Eugeny/ssh2/commit/22735cec&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Existing RSA keys are still usable—just avoid SHA-1 signatures.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/mscdex/ssh2/issues/989" target="_blank" rel="noopener">https://github.com/mscdex/ssh2/issues/989&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/mscdex/ssh2/pull/1200" target="_blank" rel="noopener">https://github.com/mscdex/ssh2/pull/1200&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://security.stackexchange.com/questions/226131/openssh-declares-ssh-rsa-deprecated-what-do-i-do-next" target="_blank" rel="noopener">https://security.stackexchange.com/questions/226131/openssh-declares-ssh-rsa-deprecated-what-do-i-do-next&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Linux File Permissions</title><link>https://en.1991421.cn/2022/07/16/linux/</link><pubDate>Sat, 16 Jul 2022 15:59:02 +0800</pubDate><guid>https://en.1991421.cn/2022/07/16/linux/</guid><description>&lt;blockquote>
&lt;p>I have been working with Linux more recently and realized my understanding of file permissions was lacking. I summarized it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="permission-basics">
&lt;a class="heading-anchor-link" href="#permission-basics">Permission basics&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="permission-basics"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When you run &lt;code>ll&lt;/code> in the terminal, you can see permissions for each file.&lt;/p>
&lt;p>For example: &lt;code>drwx------ 7 lighthouse lighthouse 4096 Jul 12 12:00 lighthouse&lt;/code>&lt;/p>
&lt;p>The first letters indicate permission info. In plain terms, they define permissions for me, my group, and others.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-07-16-160034.jpeg"
alt="https://static.1991421.cn/2022/2022-07-16-160034.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="permission-checks">
&lt;a class="heading-anchor-link" href="#permission-checks">Permission checks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="permission-checks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Given a file&amp;rsquo;s permission bits, how do you check whether a user has access?&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Get the user&amp;rsquo;s UID/GID. For example, run &lt;code>id&lt;/code> and get info for user &lt;code>lighthouse&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">uid&lt;/span>&lt;span class="o">=&lt;/span>1000&lt;span class="o">(&lt;/span>lighthouse&lt;span class="o">)&lt;/span> &lt;span class="nv">gid&lt;/span>&lt;span class="o">=&lt;/span>1000&lt;span class="o">(&lt;/span>lighthouse&lt;span class="o">)&lt;/span> &lt;span class="nv">groups&lt;/span>&lt;span class="o">=&lt;/span>1000&lt;span class="o">(&lt;/span>lighthouse&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Get the file&amp;rsquo;s UID/GID and permission bits, e.g., the &lt;code>lighthouse&lt;/code> directory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Show uid, gid&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ll -n
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwx------ &lt;span class="m">7&lt;/span> &lt;span class="m">1000&lt;/span> &lt;span class="m">1000&lt;/span> &lt;span class="m">4096&lt;/span> Jul &lt;span class="m">16&lt;/span> 15:21 lighthouse
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Show owner, group names&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwx------ &lt;span class="m">7&lt;/span> lighthouse lighthouse &lt;span class="m">4096&lt;/span> Jul &lt;span class="m">12&lt;/span> 12:00 lighthouse
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Permission logic&lt;/p>
&lt;ul>
&lt;li>If uid === 0, the user is root and has full permissions.&lt;/li>
&lt;li>If user uid === file uid, the user is the owner, so use owner rwx.&lt;/li>
&lt;li>If user is not owner but in the group, use group rwx.&lt;/li>
&lt;li>Otherwise, use other rwx.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>root is special and its UID is fixed.&lt;/li>
&lt;li>uid === 0 superuser may not be unique.&lt;/li>
&lt;/ol>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Even if root is neither owner nor in the same group and the folder has no permissions for others, root can still read/write/execute. This matches the logic above.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Learn Linux well. Keep going.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://superuser.com/questions/626843/does-the-root-account-always-have-uid-gid-0" target="_blank" rel="noopener">https://superuser.com/questions/626843/does-the-root-account-always-have-uid-gid-0&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://unix.stackexchange.com/questions/411889/what-does-it-mean-when-a-file-is-owned-by-the-user-root-and-the-group-root" target="_blank" rel="noopener">https://unix.stackexchange.com/questions/411889/what-does-it-mean-when-a-file-is-owned-by-the-user-root-and-the-group-root&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>WeChat Official Account Failed to Upload GitHub Images</title><link>https://en.1991421.cn/2022/07/01/c7b68a3/</link><pubDate>Fri, 01 Jul 2022 23:32:49 +0800</pubDate><guid>https://en.1991421.cn/2022/07/01/c7b68a3/</guid><description>&lt;blockquote>
&lt;p>The WeChat Official Account editor is notoriously difficult to use, for example, it doesn&amp;rsquo;t support Markdown. To save time, I&amp;rsquo;m used to editing and formatting in &lt;a href="https://doocs.github.io/md/" target="_blank" rel="noopener">doocs/md&lt;/a>, then copying it to the WeChat editor with one click - this step&amp;rsquo;s efficiency is resolved. But there&amp;rsquo;s another issue: the editor originally supports automatically uploading images from text to WeChat servers, but for GitHub image hosting images it shows failure. Since we can&amp;rsquo;t avoid using the WeChat editor, we need to solve this problem. Here I&amp;rsquo;ll describe my solution.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-07-01-233749.jpeg"
alt="https://static.1991421.cn/2022/2022-07-01-233749.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="failure-reason">
&lt;a class="heading-anchor-link" href="#failure-reason">Failure Reason&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="failure-reason"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let me first explain why the upload fails.&lt;/p>
&lt;p>The upload principle is that WeChat requests images based on the original image address, then stores them to its own image hosting. If the service itself doesn&amp;rsquo;t have network connectivity with &lt;code>user-images.githubusercontent.com&lt;/code>, this problem naturally occurs. For example, if WeChat servers don&amp;rsquo;t have external network access capability. I can&amp;rsquo;t say specifically what it is, but it&amp;rsquo;s definitely network restrictions.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since it&amp;rsquo;s a network restriction, the solution is to provide a proxy service to ensure WeChat services can access it, while also being able to normally access the external network, i.e., &lt;code>user-images.githubusercontent.com&lt;/code>. Here I tried using domestic cloud vendor Hong Kong region servers to build a proxy service, then batch replace the original GitHub image links in articles with my proxy service domain name. After copying to the WeChat editor, you&amp;rsquo;ll find it can upload normally.&lt;/p>
&lt;h2 id="proxy-configuration">
&lt;a class="heading-anchor-link" href="#proxy-configuration">Proxy Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="proxy-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s the proxy service setup configuration. Below is the nginx configuration, HTTP should also work, you can try it yourself.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">listen&lt;/span> &lt;span class="mi">443&lt;/span> &lt;span class="s">ssl&lt;/span> &lt;span class="s">http2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">server_name&lt;/span> &lt;span class="s">githubusercontent.xxx.cn&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1"># your domain name
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">ssl_certificate&lt;/span> &lt;span class="s">/etc/letsencrypt/live/githubusercontent.xxx.cn/fullchain.pem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">ssl_certificate_key&lt;/span> &lt;span class="s">/etc/letsencrypt/live/githubusercontent.xxx.cn/privkey.pem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">error_page&lt;/span> &lt;span class="mi">500&lt;/span> &lt;span class="mi">502&lt;/span> &lt;span class="mi">503&lt;/span> &lt;span class="mi">504&lt;/span> &lt;span class="s">/50x.html&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">location&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">/50x.html&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">root&lt;/span> &lt;span class="s">/usr/share/nginx/html&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">location&lt;/span> &lt;span class="s">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_pass&lt;/span> &lt;span class="s">https://user-images.githubusercontent.com&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="further-efficiency-improvement">
&lt;a class="heading-anchor-link" href="#further-efficiency-improvement">Further Efficiency Improvement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="further-efficiency-improvement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Manually replacing multiple domain names in the editor each time is quite troublesome. If you&amp;rsquo;re an Alfred user, you can create a workflow so that by selecting the entire text, you can update GitHub image addresses to the proxy service&amp;rsquo;s new address with one click.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-07-02-000205.jpeg"
alt="https://static.1991421.cn/2022/2022-07-02-000205.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After solving the above, you can now: write articles in MD =&amp;gt; update links to proxy service address with one click =&amp;gt; copy to WeChat editor with one click while successfully uploading images. Isn&amp;rsquo;t that great?&lt;/p></description></item><item><title>Demystifying Terminals: Emulators, Shells, and More</title><link>https://en.1991421.cn/2022/06/27/terminal-emulator/</link><pubDate>Mon, 27 Jun 2022 23:31:28 +0800</pubDate><guid>https://en.1991421.cn/2022/06/27/terminal-emulator/</guid><description>&lt;blockquote>
&lt;p>As someone who uses iTerm2 daily to manage VPS servers and run local CLI tools, I thought I understood it well. But when a designer friend asked me what iTerm2 actually &lt;em>is&lt;/em>, I found myself stumbling. The iTerm2 homepage calls it a &amp;ldquo;terminal emulator for macOS.&amp;rdquo; This sent me down a rabbit hole to clarify these foundational concepts.&lt;/p>
&lt;/blockquote>
&lt;p>Here is the breakdown of the relationship between Terminals, Shells, and Emulators:&lt;/p>
&lt;h3 id="1-the-terminal">
&lt;a class="heading-anchor-link" href="#1-the-terminal">1. The Terminal&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-the-terminal"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Historically, a &lt;strong>Terminal&lt;/strong> was a physical piece of hardware—a monitor and a keyboard—used to interact with a massive mainframe computer. Today, we use software to recreate that experience. A &lt;strong>Virtual Terminal&lt;/strong> is an application that allows one machine to access another. iTerm2 is precisely this: a virtual terminal, or more accurately, a &lt;strong>Terminal Emulator&lt;/strong>.&lt;/p>
&lt;h3 id="2-the-shell">
&lt;a class="heading-anchor-link" href="#2-the-shell">2. The Shell&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-the-shell"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The &lt;strong>Shell&lt;/strong> is the actual program that provides the command-line interface. It acts as an interpreter, taking your typed commands and passing them to the operating system&amp;rsquo;s kernel. Common shells include &lt;code>bash&lt;/code>, &lt;code>zsh&lt;/code>, and &lt;code>fish&lt;/code>. Each has its own features, syntax, and scripting language.&lt;/p>
&lt;h3 id="3-the-terminal-emulator">
&lt;a class="heading-anchor-link" href="#3-the-terminal-emulator">3. The Terminal Emulator&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-the-terminal-emulator"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The &lt;strong>Terminal Emulator&lt;/strong> is the wrapper. It is the software (like iTerm2, Alacritty, or the default Windows Terminal) that handles the graphical rendering, text selection, and window management. It hosts the shell and acts as the bridge for your input and output.&lt;/p>
&lt;hr>
&lt;h3 id="modern-context-the-web-shell">
&lt;a class="heading-anchor-link" href="#modern-context-the-web-shell">Modern Context: The &amp;ldquo;Web Shell&amp;rdquo;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="modern-context-the-web-shell"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Lately, I&amp;rsquo;ve been working on a project to access servers directly through a web browser. While we often colloquially call these &amp;ldquo;Web Shells,&amp;rdquo; they are technically &lt;strong>Web Terminals&lt;/strong>. They provide a terminal-like UI in the browser which then connects to a real shell running on a remote server.&lt;/p>
&lt;p>Understanding these distinctions helps us better appreciate the tools we use every day and communicate more clearly with our fellow developers and designers.&lt;/p></description></item><item><title>Tencent Cloud API Docset for Dash</title><link>https://en.1991421.cn/2022/06/19/apidocset/</link><pubDate>Sun, 19 Jun 2022 15:16:32 +0800</pubDate><guid>https://en.1991421.cn/2022/06/19/apidocset/</guid><description>&lt;blockquote>
&lt;p>I often have to look up Tencent Cloud APIs for work. To speed up searches, I decided to build a Dash docset—searching docs through Dash is hard to beat.&lt;/p>
&lt;/blockquote>
&lt;p>Here’s the process I followed.&lt;/p>
&lt;h2 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022-06-19-230436.gif"
alt="https://static.1991421.cn/2022/2022-06-19-230436.gif"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="how-it-works">
&lt;a class="heading-anchor-link" href="#how-it-works">How It Works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-it-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Crawl the Tencent Cloud API docs and save the pages as HTML.
&lt;ul>
&lt;li>Every Dash docset, whether official or community-built, essentially bundles fetched HTML pages or Markdown sources. Markdown is easier to process because of its structure.&lt;/li>
&lt;li>Tencent Cloud doesn’t publish Markdown files, so crawling HTML is the only route.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Walk through all downloaded pages and extract interfaces, functions, titles, and hierarchy information, which can be inferred from the original URLs.
&lt;ul>
&lt;li>As long as the markup is consistent, parsing isn’t difficult.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Store the extracted data in Dash’s prescribed SQLite schema and package it as a docset.&lt;/li>
&lt;/ol>
&lt;p>Once the direction was clear, implementation was straightforward.&lt;/p>
&lt;h2 id="build-steps">
&lt;a class="heading-anchor-link" href="#build-steps">Build Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Use &lt;code>wget&lt;/code> to mirror the site—the command supports recursive downloads.
&lt;ul>
&lt;li>&lt;code>curl&lt;/code> can’t recurse, so it’s out.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Create the docset folder structure and SQLite tables that Dash expects.&lt;/li>
&lt;li>Parse the HTML files with Node.js to extract metadata and insert rows into the database.
&lt;ul>
&lt;li>I first considered parsing everything in memory and bulk-inserting, but with so many files it risked running out of memory. Instead, I parse one page at a time and insert after each batch.&lt;/li>
&lt;li>Dash can host local HTML or link out to the live site. Local copies give you full control over layout, while remote URLs keep things simple but inherit the site’s styling. I opted for remote URLs: both the index and the API pages point to Tencent Cloud’s live docs.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Tar the directory into the &lt;code>.docset&lt;/code> bundle.&lt;/li>
&lt;li>Write a &lt;code>feed.xml&lt;/code> so others can subscribe.
&lt;ul>
&lt;li>Dash doesn’t sync locally added docsets across devices. Packaging a feed lets other machines subscribe, or you can submit it to Dash’s official repo.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>All scripts and the docset itself live here: &lt;a href="https://github.com/alanhe421/dash-docset-tcapi" target="_blank" rel="noopener">dash-docset-tcapi&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>This approach focuses on fast keyword lookup that redirects to the live doc URLs. Serving local HTML is possible, but it risks going stale and requires extra styling work; you’d also have to decide whether to keep URLs local or remote.&lt;/li>
&lt;li>Any API documentation lacking an existing docset can use the same playbook.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://kapeli.com/docsets" target="_blank" rel="noopener">https://kapeli.com/docsets&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/technosophos/dashing#readme" target="_blank" rel="noopener">https://github.com/technosophos/dashing#readme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://kapeli.com/docsets#scriptExamples" target="_blank" rel="noopener">https://kapeli.com/docsets#scriptExamples&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Getting Started with ssh2-sftp-client</title><link>https://en.1991421.cn/2022/06/12/ssh2-sftp-client/</link><pubDate>Sun, 12 Jun 2022 13:24:32 +0800</pubDate><guid>https://en.1991421.cn/2022/06/12/ssh2-sftp-client/</guid><description>&lt;blockquote>
&lt;p>While building a WebShell, I needed more than sz/rz uploads. I wanted a GUI that could list files and handle uploads/downloads. After some research I chose &lt;a href="https://github.com/theophilusx/ssh2-sftp-client" target="_blank" rel="noopener">ssh2-sftp-client&lt;/a>, which wraps SFTP/SSH. Here are the issues I ran into and how I solved them.&lt;/p>
&lt;/blockquote>
&lt;h2 id="enabling-or-disabling-the-service">
&lt;a class="heading-anchor-link" href="#enabling-or-disabling-the-service">Enabling or Disabling the Service&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enabling-or-disabling-the-service"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Most Linux distributions ship with SFTP enabled because it’s part of the SSH stack. Administrators can disable it, though, so your code must handle unavailable services.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># vi /etc/ssh/sshd_config&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># override default of no subsystems&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Subsystem sftp /usr/libexec/openssh/sftp-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">After
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># override default of no subsystems&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Subsystem sftp /usr/libexec/openssh/sftp-server&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service sshd restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If SFTP is disabled, ssh2-sftp-client throws &lt;code>ERR_GENERIC_CLIENT&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-06-12-230743.jpeg"
alt="https://static.1991421.cn/2022/2022-06-12-230743.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="list">
&lt;a class="heading-anchor-link" href="#list">&lt;code>list&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="list"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use &lt;code>list()&lt;/code> to retrieve directory contents, but keep these in mind:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;code>~&lt;/code> is not supported. Paths must be absolute or relative—not aliases.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">fileList&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">remotePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fileList&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">end&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>The &lt;code>type&lt;/code> field describes the entry:&lt;/p>
&lt;ul>
&lt;li>&lt;code>d&lt;/code>: directory&lt;/li>
&lt;li>&lt;code>-&lt;/code>: regular file&lt;/li>
&lt;li>&lt;code>l&lt;/code>: symlink&lt;/li>
&lt;/ul>
&lt;p>For symlinks, you must query again (e.g., via &lt;code>stat&lt;/code>) to determine whether the target is a file or directory—&lt;code>isDirectory&lt;/code> / &lt;code>isFile&lt;/code> clarify the real type.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>size&lt;/code> is in bytes, similar to standard shell output. Directories also have a size.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>rights&lt;/code> expose permissions. The response also includes the owning UID (&lt;code>owner&lt;/code>) and GID (&lt;code>group&lt;/code>). To determine the current user’s access, you need that user’s UID/GID. Run &lt;code>id &amp;lt;username&amp;gt;&lt;/code> over SSH (with the &lt;code>ssh2&lt;/code> client, not the SFTP wrapper) to retrieve it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Client&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">SSH2Client&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ssh2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">ssh2Client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">SSH2Client&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ssh2Client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ready&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ssh2Client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`id &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">username&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">stream&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">stream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">buf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">idRes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">buf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">idRes&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// uid=0(root) gid=0(root) groups=0(root)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">matches&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">idRes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/\d+/g&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">uid&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="nx">matches&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">gid&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="nx">matches&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">idRes&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Combine that with the &lt;code>list()&lt;/code> response to decide whether the user can read/write.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="filter">
&lt;a class="heading-anchor-link" href="#filter">&lt;code>filter&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="filter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>list(path, filter)&lt;/code> accepts a regex-like filter. &lt;code>^[^.]&lt;/code> hides dotfiles. Note: Windows hidden files don’t follow the dot convention, so this method won’t hide them—you’d need to query attributes separately.&lt;/p>
&lt;h3 id="response-payload">
&lt;a class="heading-anchor-link" href="#response-payload">Response Payload&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="response-payload"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;-&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;admin1.pem&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;size&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">418&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;modifyTime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1651807713000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;accessTime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1651807715000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rights&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;rw&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;group&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;other&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;r&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;owner&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;group&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="put-vs-fastput">
&lt;a class="heading-anchor-link" href="#put-vs-fastput">&lt;code>put&lt;/code> vs. &lt;code>fastPut&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="put-vs-fastput"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Both upload files. &lt;code>fastPut&lt;/code> always reads from disk—meaning temporary files if you’re proxying uploads through a Node server. That hurts speed and makes progress reporting tricky. For streaming scenarios (like a WebSocket upload piped through Node to the remote host), use &lt;code>put&lt;/code>.&lt;/li>
&lt;li>&lt;code>put&lt;/code> accepts any readable stream.&lt;/li>
&lt;li>Example: Convert chunks received over WebSocket into a readable stream and pipe directly to &lt;code>put&lt;/code>. The Node server doesn’t persist anything locally.&lt;/li>
&lt;li>Set permissions explicitly—for example, &lt;code>0o644&lt;/code>, matching FileZilla’s SFTP defaults (&lt;code>rw-r--r--&lt;/code>).&lt;/li>
&lt;/ol>
&lt;h2 id="get-vs-fastget">
&lt;a class="heading-anchor-link" href="#get-vs-fastget">&lt;code>get&lt;/code> vs. &lt;code>fastGet&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="get-vs-fastget"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Similar to uploads: &lt;code>get&lt;/code> is better when you need to stream data to the frontend; &lt;code>fastGet&lt;/code> writes files directly to disk.&lt;/p>
&lt;h2 id="throttling">
&lt;a class="heading-anchor-link" href="#throttling">Throttling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="throttling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use a throttling stream to limit bandwidth:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Throttle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;throttle&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">throttleStream&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Throttle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1024&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 1 MB/s
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">throttleStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">stream&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeStreamOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClose&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mo">0o644&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readStreamOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">autoClose&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pipeOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">end&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="cancel-transfers">
&lt;a class="heading-anchor-link" href="#cancel-transfers">Cancel Transfers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cancel-transfers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To abort an upload/download, close the SFTP connection and reconnect.&lt;/p>
&lt;h2 id="resumable-transfers">
&lt;a class="heading-anchor-link" href="#resumable-transfers">Resumable Transfers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resumable-transfers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;strong>Download:&lt;/strong> set &lt;code>readStreamOptions.start&lt;/code> to the byte offset.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">sftp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">remoteFile&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fileWtr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">readStreamOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">start&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Upload:&lt;/strong> use &lt;code>append()&lt;/code> to resume.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="downloaddir--uploaddir">
&lt;a class="heading-anchor-link" href="#downloaddir--uploaddir">&lt;code>downloadDir&lt;/code> / &lt;code>uploadDir&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="downloaddir--uploaddir"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>These helpers work but can’t stream, so the server must store intermediate files and progress reporting is inaccurate. Instead, recursively iterate directories and use &lt;code>get&lt;/code>/&lt;code>put&lt;/code>. Create folders with &lt;code>mkdir&lt;/code> as needed.&lt;/p>
&lt;h2 id="sftp-primer">
&lt;a class="heading-anchor-link" href="#sftp-primer">SFTP Primer&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sftp-primer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://www.ssh.com/academy/ssh/sftp" target="_blank" rel="noopener">https://www.ssh.com/academy/ssh/sftp&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s it—hope it saves you some time.&lt;/p></description></item><item><title>Blank Images While Drawing Mini Program Posters</title><link>https://en.1991421.cn/2022/05/29/miniprogram-poster-image-blank/</link><pubDate>Sun, 29 May 2022 11:04:52 +0800</pubDate><guid>https://en.1991421.cn/2022/05/29/miniprogram-poster-image-blank/</guid><description>&lt;blockquote>
&lt;p>Our mini program generates posters by downloading multiple web images (PNG/GIF) and drawing them via &lt;a href="https://github.com/Kujiale-Mobile/Painter" target="_blank" rel="noopener">Painter&lt;/a>. Some posters randomly showed black or blank regions. Here’s what we found.&lt;/p>
&lt;/blockquote>
&lt;h2 id="symptoms">
&lt;a class="heading-anchor-link" href="#symptoms">Symptoms&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="symptoms"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Certain images rendered as black, while others displayed correctly.&lt;/li>
&lt;li>Network traces on real devices showed missing image requests for the blank regions.&lt;/li>
&lt;/ul>
&lt;h2 id="investigation">
&lt;a class="heading-anchor-link" href="#investigation">Investigation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="investigation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Painter downloads remote assets into the mini program’s temp directory, then caches them with an LRU strategy (6 MB cap by default).&lt;/li>
&lt;li>Large GIFs (~23 MB) triggered the LRU cleanup logic:
&lt;ul>
&lt;li>PNG downloads completed first (fast, small).&lt;/li>
&lt;li>When the GIF finished, Painter attempted to cache it; the GIF exceeded the 6 MB limit.&lt;/li>
&lt;li>The LRU logic deleted existing temp files (including the PNG) to stay under the cap.&lt;/li>
&lt;li>Subsequent canvas draws referenced files that had been deleted, resulting in blank regions.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Upon re-entering the poster screen, Painter reused cached paths without re-downloading, but the referenced files no longer existed—leading to empty drawings.&lt;/li>
&lt;/ol>
&lt;h2 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Upgrading to the latest Painter where &lt;code>lru&lt;/code> defaults to &lt;code>false&lt;/code> resolved the issue in our case.&lt;/li>
&lt;/ul>
&lt;h2 id="lessons--workarounds">
&lt;a class="heading-anchor-link" href="#lessons--workarounds">Lessons &amp;amp; Workarounds&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lessons--workarounds"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Painter’s download + LRU deletion runs asynchronously. Large files can evict required assets before the canvas draw executes.&lt;/li>
&lt;li>Options:
&lt;ol>
&lt;li>Disable LRU (but monitor the mini program’s overall temp storage, capped at ~200 MB).&lt;/li>
&lt;li>Customize Painter to run downloads sequentially or to increase the cache threshold.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;p>Despite the fix, be aware that once temp storage hits 200 MB, downloads may fail. Implement cleanup or increase the threshold if you fork Painter.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/framework/ability/file-system.html" target="_blank" rel="noopener">https://developers.weixin.qq.com/miniprogram/dev/framework/ability/file-system.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html" target="_blank" rel="noopener">https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Kujiale-Mobile/Painter" target="_blank" rel="noopener">Painter repository&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Kujiale-Mobile/Painter/issues/300" target="_blank" rel="noopener">Related issue&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>npm Development Dependencies</title><link>https://en.1991421.cn/2022/05/26/npm-dev-dependencies/</link><pubDate>Thu, 26 May 2022 09:53:59 +0800</pubDate><guid>https://en.1991421.cn/2022/05/26/npm-dev-dependencies/</guid><description>&lt;p>When managing package dependencies, there are both dependencies and development dependencies. Third-party packages that are installed also have their own dependencies and development dependencies. The installation logic for these package dependencies needs to be clarified.&lt;/p>
&lt;h2 id="npm-i">
&lt;a class="heading-anchor-link" href="#npm-i">npm i&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm-i"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>During local development, we typically run &lt;code>npm i&lt;/code>, which installs both direct dependencies and direct development dependencies. However, &lt;code>development dependencies of third-party packages are not installed&lt;/code>. This makes sense because packages themselves are published and considered to be in production mode, so there&amp;rsquo;s no need to install their development dependencies.&lt;/p>
&lt;p>Of course, with dependencies and indirect dependencies plus package versions, multiple versions of the same package can coexist, which is also important to note.&lt;/p>
&lt;h2 id="--production">
&lt;a class="heading-anchor-link" href="#--production">&amp;ndash;production&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="--production"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When running &lt;code>npm i&lt;/code>, you can specify the mode explicitly. In production mode, development dependencies are not installed. This is mainly used when deploying Node.js services to production, where development-related packages (like TypeScript) are often not needed. This can further improve deployment speed.&lt;/p></description></item><item><title>Implementing sz/rz Upload/Download in WebShell</title><link>https://en.1991421.cn/2022/05/17/webshellsz-rz/</link><pubDate>Tue, 17 May 2022 22:57:20 +0800</pubDate><guid>https://en.1991421.cn/2022/05/17/webshellsz-rz/</guid><description>&lt;blockquote>
&lt;p>I recently added upload/download support and explored &lt;code>lrzsz&lt;/code>. Here are the notes.&lt;/p>
&lt;/blockquote>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, understand what sz/rz do:&lt;/p>
&lt;ol>
&lt;li>&lt;code>sz&lt;/code> supports multiple-file downloads.&lt;/li>
&lt;li>&lt;code>rz&lt;/code> supports multiple-file uploads. The upload target directory is the current working directory at the time the command is triggered.&lt;/li>
&lt;li>&lt;code>rz&lt;/code> does not support uploading folders.&lt;/li>
&lt;li>For &lt;code>rz&lt;/code>, when a file already exists, the UI will indicate it.&lt;/li>
&lt;li>File size limit: cannot transfer files larger than &lt;code>4 GB&lt;/code>.&lt;/li>
&lt;li>Both download and upload support cancel.&lt;/li>
&lt;/ol>
&lt;h2 id="system-support-for-szrz">
&lt;a class="heading-anchor-link" href="#system-support-for-szrz">System Support for sz/rz&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="system-support-for-szrz"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Servers typically don’t have lrzsz installed; install it manually.&lt;/li>
&lt;li>Not every tool supports rz and sz—you must use a tool that supports the ZModem protocol. Installation scripts:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># macOS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew remove lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># CentOS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yum install lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yum remove lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## Cross-platform install script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo sh -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://gist.githubusercontent.com/alanhe421/6a299b815f4dd3d242abc16b8be6b861/raw/dbe1497208f1d968ed8b67cad09c596e35c5be9c/install-package-lrzsz.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="official-demo">
&lt;a class="heading-anchor-link" href="#official-demo">Official Demo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="official-demo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The zmodem.js author provides a demo. To run it:&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>Demo: &lt;a href="https://github.com/FGasper/xterm.js/tree/zmodem" target="_blank" rel="noopener">https://github.com/FGasper/xterm.js/tree/zmodem&lt;/a>&lt;/li>
&lt;li>Switch Node to &lt;code>v8&lt;/code> and re-run &lt;code>npm i&lt;/code>&lt;/li>
&lt;li>Start and visit http://127.0.0.1:3000/&lt;/li>
&lt;/ol>
&lt;h2 id="implementation-details">
&lt;a class="heading-anchor-link" href="#implementation-details">Implementation Details&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-details"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The author’s demo is minimal and omits some features. Here are key parts. Full example: &lt;a href="https://github.com/alanhe421/express-demo" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;h2 id="cancel-sz-download">
&lt;a class="heading-anchor-link" href="#cancel-sz-download">Cancel sz download&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cancel-sz-download"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When the system file picker is invoked for upload, canceling it isn’t observable in JS. The user must press &lt;code>Ctrl+C&lt;/code>, then send an abort command to cancel.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">activeZsession&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_skip&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Alternatively, build your own upload dialog and call abort when the user cancels.&lt;/p>
&lt;h2 id="cancel-rz-upload">
&lt;a class="heading-anchor-link" href="#cancel-rz-upload">Cancel rz upload&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cancel-rz-upload"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Stop sending chunks (xfer.send(chunk))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">await&lt;/span> &lt;span class="nx">zsession&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="upload-file-already-exists">
&lt;a class="heading-anchor-link" href="#upload-file-already-exists">Upload: file already exists&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upload-file-already-exists"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If &lt;code>xfer&lt;/code> is falsy, the server rejected the upload—often because the file exists (other reasons are possible).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">xfer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">zsession&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send_offer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">curb&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">xfer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">showMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">xfer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get_details&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> rejected.`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="progress-display">
&lt;a class="heading-anchor-link" href="#progress-display">Progress display&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="progress-display"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>For upload, compute progress as sentSize/totalSize; for download, receivedSize/totalSize.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>In upload loops, writing progress directly may not work because xterm output is async; add a small delay:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>For better UX, render a progress bar; avoid &lt;code>\n&lt;/code> when calling &lt;code>xterm.write&lt;/code> to keep it on one line.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">getProgressBar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">total&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">current&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">total&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">current&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;total must be greater than current&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">progressBarLength&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// bar length
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">progress&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">floor&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">total&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">progressBarLength&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">empty&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">progressBarLength&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nx">progress&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">progressBar&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;█&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">repeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">progress&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;░&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">repeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">empty&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="sb">`(&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">progressBar&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">) &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">total&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toFixed&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">%`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="useful-resources">
&lt;a class="heading-anchor-link" href="#useful-resources">Useful Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="useful-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="https://juejin.cn/post/6935621453400244260" target="_blank" rel="noopener">https://juejin.cn/post/6935621453400244260&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://wsgzao.github.io/post/lrzsz/" target="_blank" rel="noopener">https://wsgzao.github.io/post/lrzsz/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://qa.1r1g.com/sf/ask/672770031/" target="_blank" rel="noopener">https://qa.1r1g.com/sf/ask/672770031/&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Extending JetBrains Toolbox App Browser Extension</title><link>https://en.1991421.cn/2022/05/15/2eeeae1/</link><pubDate>Sun, 15 May 2022 22:12:15 +0800</pubDate><guid>https://en.1991421.cn/2022/05/15/2eeeae1/</guid><description>&lt;blockquote>
&lt;p>JetBrains&amp;rsquo; browser plugin can achieve one-click checkout repo to open JB IDE, but doesn&amp;rsquo;t support domestic Git services like Gitee and Tencent Gongfeng. Understanding that the plugin&amp;rsquo;s code is open source, I decided to extend support for these services.&lt;/p>
&lt;/blockquote>
&lt;h2 id="results">
&lt;a class="heading-anchor-link" href="#results">Results&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="results"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-17-224737.gif"
alt="https://static.1991421.cn/2022/2022-05-17-224737.gif"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/WC88PPo.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/KUqhin4.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Download link: &lt;a href="https://github.com/alanhe421/toolbox-browser-extensionx" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;h2 id="principle">
&lt;a class="heading-anchor-link" href="#principle">Principle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="principle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s a brief explanation of the plugin&amp;rsquo;s basic principle. For detailed source code, check the repo.&lt;/p>
&lt;ol>
&lt;li>JB IDE itself provides URL Schema method to trigger actions, like checkout here. But the official doesn&amp;rsquo;t provide a complete action list, so based on source code I&amp;rsquo;ve collected some and organized them in &lt;a href="https://github.com/alanhe421/jetbrains-url-schemes" target="_blank" rel="noopener">jetbrains-url-schemes&lt;/a>&lt;/li>
&lt;li>Plugin executes some actions when hitting target git web pages
&lt;ol>
&lt;li>Extract repo, user and other info from URL to determine clone repo address&lt;/li>
&lt;li>Analyze programming languages involved in the repo through separate API or partial info from web pages, then determine which IDE to use through language and percentage weighting&lt;/li>
&lt;li>Append button at target location&lt;/li>
&lt;li>Implement URL Schema jump when user clicks button&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Through this extension support, you can now seamlessly experience the convenience of plugin to IDE.&lt;/p></description></item><item><title>Telegram Bot Development</title><link>https://en.1991421.cn/2022/05/04/telegram-bot/</link><pubDate>Wed, 04 May 2022 11:19:20 +0800</pubDate><guid>https://en.1991421.cn/2022/05/04/telegram-bot/</guid><description>&lt;blockquote>
&lt;p>For technical and non-technical problems I encounter, besides writing blog summaries, I often record them as GitHub issues in &lt;code>alanhg/others-note&lt;/code> and &lt;code>alanhg/coding-note&lt;/code>. Searching across repos is inefficient, so I decided to build a Telegram Bot to search issues across repos. I didn’t find an existing solution, so I implemented one myself.&lt;/p>
&lt;/blockquote>
&lt;h2 id="current-result">
&lt;a class="heading-anchor-link" href="#current-result">Current Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use the &lt;code>repoadd&lt;/code> command to add target repositories, then enter any keyword to search issues across multiple repos. Demo:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-04-114501.gif"
alt="https://static.1991421.cn/2022/2022-05-04-114501.gif"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Bot link: &lt;a href="https://t.me/github_issue_1_bot" target="_blank" rel="noopener">GitHub Issue Bot&lt;/a>&lt;/p>
&lt;h2 id="bot-development">
&lt;a class="heading-anchor-link" href="#bot-development">Bot Development&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bot-development"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I hit a few bumps while building the bot; here’s a summary:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Create the bot with &lt;a href="https://t.me/BotFather" target="_blank" rel="noopener">&lt;code>@BotFather&lt;/code>&lt;/a> and follow the interactive setup.&lt;/p>
&lt;ul>
&lt;li>Configure bot name/avatar and command list in BotFather.&lt;/li>
&lt;li>Keep the &lt;code>bot_token&lt;/code> safe; it identifies your bot and authorizes messaging.&lt;/li>
&lt;li>To create a command list, use BotFather’s “Edit Commands” for the target bot. Notes:
&lt;ul>
&lt;li>Avoid hyphens in command names.&lt;/li>
&lt;li>Don’t include a leading &lt;code>/&lt;/code> in the definition; the slash is only for invocation.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-04-115842.jpeg"
alt="https://static.1991421.cn/2022/2022-05-04-115842.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>You can’t add commands incrementally; enter the full set each time.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>There are solid helper libraries to get started.&lt;/p>
&lt;ul>
&lt;li>For Node.js, use &lt;a href="https://github.com/yagop/node-telegram-bot-api" target="_blank" rel="noopener">node-telegram-bot-api&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Bots support markdown/html message formats, which simplifies layout.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>To update a “searching…” message with results, use &lt;code>bot.editMessageText&lt;/code> with explicit &lt;code>message_id&lt;/code> and &lt;code>chat_id&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>To listen for replies to a specific message, use &lt;code>bot.onReplyToMessage&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Deploy the bot on a publicly reachable host; you may also need a proxy during testing.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With this bot, I can simply enter a keyword to check for similar issues across repos.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/yagop/node-telegram-bot-api" target="_blank" rel="noopener">https://github.com/yagop/node-telegram-bot-api&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Difference Between --help and man Commands</title><link>https://en.1991421.cn/2022/05/03/helpman/</link><pubDate>Tue, 03 May 2022 00:44:53 +0800</pubDate><guid>https://en.1991421.cn/2022/05/03/helpman/</guid><description>&lt;blockquote>
&lt;p>When looking up command tool parameters, we often use &lt;code>--help&lt;/code>, such as &lt;code>rsync --help&lt;/code>. Alternatively, we can use the man command, like &lt;code>man rsync&lt;/code>. So what&amp;rsquo;s the difference between them, and how should we choose?&lt;/p>
&lt;/blockquote>
&lt;h2 id="differences">
&lt;a class="heading-anchor-link" href="#differences">Differences&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="differences"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>The man command provides comprehensive usage manuals for all commands available in the system, supported on both Linux and Mac&lt;/p>
&lt;ul>
&lt;li>Non-system commands are generally not available, such as &lt;code>man java&lt;/code>&lt;/li>
&lt;li>&amp;ldquo;man&amp;rdquo; is short for manual, meaning help documentation&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;code>--help&lt;/code> is a parameter provided by each command itself, used to print command usage instructions, but some commands may not provide it, such as &lt;code>ssh&lt;/code>&lt;/p>
&lt;p>SSH has a special case where the command&amp;rsquo;s own parameter descriptions are quite brief (like the t parameter without detailed explanation), while man provides much clearer descriptions&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-03-005726.jpeg"
alt="https://static.1991421.cn/2022/2022-05-03-005726.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-03-010520.jpeg"
alt="https://static.1991421.cn/2022/2022-05-03-010520.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="usage-recommendations">
&lt;a class="heading-anchor-link" href="#usage-recommendations">Usage Recommendations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-recommendations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>For system commands, prioritize using man to view documentation&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For non-system commands, use the command&amp;rsquo;s own &lt;code>--help&lt;/code> documentation&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding the distinction between &lt;code>--help&lt;/code> and &lt;code>man&lt;/code> commands is essential for efficient command-line usage. While &lt;code>--help&lt;/code> provides quick, command-specific information, &lt;code>man&lt;/code> offers comprehensive system-wide documentation. The choice depends on whether you&amp;rsquo;re working with system commands (prefer &lt;code>man&lt;/code>) or third-party tools (prefer &lt;code>--help&lt;/code>). Both tools serve complementary purposes in the Linux/Mac command-line ecosystem.&lt;/p></description></item><item><title>Migrating Blog Service to Tencent Cloud Hong Kong</title><link>https://en.1991421.cn/2022/05/02/migrate-blog-to-tencent-hk/</link><pubDate>Mon, 02 May 2022 11:13:56 +0800</pubDate><guid>https://en.1991421.cn/2022/05/02/migrate-blog-to-tencent-hk/</guid><description>&lt;blockquote>
&lt;p>My blog platform moved from GitHub Pages → Tencent Cloud (China) → Vultr Japan. Now a fourth move to Tencent Cloud Hong Kong.&lt;/p>
&lt;/blockquote>
&lt;p>Reasons to choose Tencent Cloud HK:&lt;/p>
&lt;ol>
&lt;li>Relatively lower costs&lt;/li>
&lt;li>The Tencent Cloud Hong Kong node has several advantages
&lt;ul>
&lt;li>Can be used for scientific internet access (requires WS/V2Ray)&lt;/li>
&lt;li>No website filing requirements&lt;/li>
&lt;li>Low latency, noticeably faster than Vultr Japan&lt;/li>
&lt;li>流量足，比如我当前的机器，一个月2T&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>基于此做下迁移&lt;/p>
&lt;h2 id="migration-steps">
&lt;a class="heading-anchor-link" href="#migration-steps">Migration steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="migration-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>直接SCP命令将服务器上的资源直接拷贝过去&lt;/li>
&lt;li>修改域名DNS记录&lt;/li>
&lt;li>修改GitHub CD部署目标机器地址&lt;/li>
&lt;/ol>
&lt;p>Deployment took about an hour; speed looks good.&lt;/p></description></item><item><title>iTerm2 integrated with Tmux</title><link>https://en.1991421.cn/2022/05/01/iterm2tmux/</link><pubDate>Sun, 01 May 2022 21:49:31 +0800</pubDate><guid>https://en.1991421.cn/2022/05/01/iterm2tmux/</guid><description>&lt;blockquote>
&lt;p>Recently, when using an open source tool, I found a tool called tmux. Then I found that Tmux is quite popular, the repo star has 20K+, and my commonly used iTerm2 is also integrated with tmux. Based on this, it is necessary to understand tmux and clarify the usage scenarios.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-01-234555.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Since the current use is still shallow, please point out if any mistakes&lt;/p>
&lt;h2 id="concept">
&lt;a class="heading-anchor-link" href="#concept">Concept&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concept"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Start with the concept and understand its function.&lt;/p>
&lt;blockquote>
&lt;p>tmux is a terminal multiplexer. It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>Personal understanding is that tmux saves the terminal session on-site. For example, SSH is connected to a VPS, vi edit a file, directly closes the session, and then reopens the connection, you will find that the state is lost, you need to re-enter the target path and reopen it edit. With tmux, you can save this session, re-access the target machine, load the previously saved session, and everything will be restored as before.&lt;/li>
&lt;li>The reason why tmux can save sessions is because after tmux is installed on the target server, a separate service will be started for on-site saving.&lt;/li>
&lt;/ol>
&lt;h2 id="tmux-installation">
&lt;a class="heading-anchor-link" href="#tmux-installation">Tmux Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tmux-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understand the above principles, you can understand that tmux is not necessarily installed locally, but &lt;code>installed on the machine that needs to save the session&lt;/code>.&lt;/p>
&lt;p>The installation commands in common operating system environments are as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># macOS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install tmux
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># centos
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yum install tmux
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;!-- more -->
&lt;h2 id="iterm2-installation">
&lt;a class="heading-anchor-link" href="#iterm2-installation">iTerm2 Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="iterm2-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After Tmux is installed, followed by the settings of the local terminal App&lt;/p>
&lt;ol>
&lt;li>Global tmux settings&lt;/li>
&lt;/ol>
&lt;img src="https://static.1991421.cn/2022/2022-05-01-233054.jpeg" style="zoom:67%;" />
&lt;p>The picture shows my current settings, basically keep the default settings.&lt;/p>
&lt;ol start="2">
&lt;li>
&lt;p>Individual profile configuration startup command&lt;/p>
&lt;p>For the machine where tmux needs to be enabled, the command configuration is as follows.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Connect to the target machine, then execute the tmux session load, if not, open a new session&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ssh&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">p&lt;/span> &lt;span class="mi">22&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="n">root&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="mf">123.160&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">122.79&lt;/span> &lt;span class="s2">&amp;#34;tmux -CC attach || tmux -CC&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;img src="https://static.1991421.cn/2022/2022-05-01-233303.jpeg" style="zoom: 67%;" />
&lt;/li>
&lt;/ol>
&lt;p>It can be used after setting as above.&lt;/p>
&lt;h2 id="write-at-the-end">
&lt;a class="heading-anchor-link" href="#write-at-the-end">Write at the End&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="write-at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Searching for information, you will find that tmux is very rich in configuration and can be very complicated to use, which invisibly increases the mental burden, but you don&amp;rsquo;t need to worry about it.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>In the case of using iTerm2, operations such as split screen and session opening are already available. iTerm2 itself has mapped many functions of tmux, so it is enough to maintain the operating habits of iTerm2. After in-depth use, if there are still insufficient Fill in the gaps.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.ruanyifeng.com/blog/2019/10/tmux.html" target="_blank" rel="noopener">https://www.ruanyifeng.com/blog/2019/10/tmux.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://iterm2.com/documentation-tmux-integration.html" target="_blank" rel="noopener">https://iterm2.com/documentation-tmux-integration.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/tmux/tmux" target="_blank" rel="noopener">https://github.com/tmux/tmux&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Domain Transfer In/Out</title><link>https://en.1991421.cn/2022/04/30/domain-transfer-in-out/</link><pubDate>Sat, 30 Apr 2022 00:00:08 +0800</pubDate><guid>https://en.1991421.cn/2022/04/30/domain-transfer-in-out/</guid><description>&lt;blockquote>
&lt;p>I seldom purchase servers in mainland China, but I still register domains there—it’s cheaper and management tools are friendly. I recently moved a domain from Aliyun to Tencent Cloud, so I documented the transfer steps.&lt;/p>
&lt;/blockquote>
&lt;img src="https://static.1991421.cn/2022/2022-04-30-102218.jpeg" style="zoom:50%;" />
&lt;h2 id="process">
&lt;a class="heading-anchor-link" href="#process">Process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>On Aliyun’s transfer-out page, request the &lt;strong>Authorization Code&lt;/strong>.&lt;/li>
&lt;li>On Tencent Cloud’s &lt;a href="https://console.cloud.tencent.com/domain/trans-in/add" target="_blank" rel="noopener">transfer-in page&lt;/a>, submit the domain + authorization code.
&lt;ul>
&lt;li>Transfer-in typically takes &lt;strong>4–6 business days&lt;/strong> (mine finished in one day).&lt;/li>
&lt;li>Afterward, switch DNS to Tencent’s recommended servers.&lt;/li>
&lt;li>DNS records do &lt;strong>not&lt;/strong> migrate automatically; recreate them manually.&lt;/li>
&lt;li>Transfers are free, but Tencent charges one year of renewal upfront—even if the domain hasn’t expired.&lt;/li>
&lt;li>While the status is “transferring,” you can’t edit DNS at Tencent yet, but Aliyun still allows configuration changes.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>After that, wait for the transfer to complete.&lt;/p>
&lt;h2 id="godaddy">
&lt;a class="heading-anchor-link" href="#godaddy">GoDaddy?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="godaddy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I’ve used GoDaddy as well. Transfers there differ slightly:&lt;/p>
&lt;ul>
&lt;li>For transfer-in, search the domain first and proceed with the authorization code.&lt;/li>
&lt;li>For transfer-out, initiate it from the domain management page, similar to Aliyun/Tencent.&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Tencent Cloud transfer-in: &lt;a href="https://cloud.tencent.com/document/product/242/3645?from=10680" target="_blank" rel="noopener">https://cloud.tencent.com/document/product/242/3645?from=10680&lt;/a>&lt;/li>
&lt;li>Aliyun transfer-out: &lt;a href="https://help.aliyun.com/document_detail/35876.html" target="_blank" rel="noopener">https://help.aliyun.com/document_detail/35876.html&lt;/a>&lt;/li>
&lt;li>GoDaddy transfer-in: &lt;a href="https://sg.godaddy.com/zh/help/transfer-my-domain-to-godaddy-1592" target="_blank" rel="noopener">https://sg.godaddy.com/zh/help/transfer-my-domain-to-godaddy-1592&lt;/a>&lt;/li>
&lt;li>GoDaddy transfer-out: &lt;a href="https://sg.godaddy.com/zh/help/transfer-my-domain-away-from-godaddy-3560" target="_blank" rel="noopener">https://sg.godaddy.com/zh/help/transfer-my-domain-away-from-godaddy-3560&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Applying for GitHub Sponsor</title><link>https://en.1991421.cn/2022/04/25/github-sponsor/</link><pubDate>Mon, 25 Apr 2022 23:01:10 +0800</pubDate><guid>https://en.1991421.cn/2022/04/25/github-sponsor/</guid><description>&lt;blockquote>
&lt;p>Maintaining open-source projects requires effort, and monetary rewards are a great incentive. GitHub now provides a sponsor mechanism allowing you to support project developers easily. Here&amp;rsquo;s how to proceed.&lt;/p>
&lt;/blockquote>
&lt;h2 id="sponsoring-others">
&lt;a class="heading-anchor-link" href="#sponsoring-others">Sponsoring Others&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sponsoring-others"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-04-25-230601.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>On the right side of a project, under &amp;ldquo;Sponsor this project,&amp;rdquo; click on the ❤️ next to each specific developer to sponsor them. You can choose between one-time payments or regular payments.&lt;/p>
&lt;h2 id="applying-for-sponsorship">
&lt;a class="heading-anchor-link" href="#applying-for-sponsorship">Applying for Sponsorship&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="applying-for-sponsorship"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, you need to apply for sponsor permissions. The application address is &lt;a href="https://github.com/sponsors" target="_blank" rel="noopener">https://github.com/sponsors&lt;/a>&lt;/p>
&lt;h3 id="enable-sponsor-support">
&lt;a class="heading-anchor-link" href="#enable-sponsor-support">Enable Sponsor Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-sponsor-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="note">Note&lt;/h4>&lt;ol>
&lt;li>The country currently &lt;code>does not support mainland China&lt;/code> but supports Hong Kong. For the list of supported countries, &lt;a href="https://github.com/sponsors" target="_blank" rel="noopener">see here - scroll to the bottom&lt;/a>&lt;/li>
&lt;li>If there is no status update after applying, you can submit a ticket to &lt;a href="https://support.github.com/request" target="_blank" rel="noopener">GitHub Support&lt;/a> to inquire.&lt;/li>
&lt;li>The payment account currently only supports Stripe, so you must open a Stripe account. Since Stripe does not provide services in mainland China, you need to apply for a Stripe account in Hong Kong/Japan.&lt;/li>
&lt;/ol>
&lt;h3 id="repo-sponsorship-configuration">
&lt;a class="heading-anchor-link" href="#repo-sponsorship-configuration">Repo Sponsorship Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="repo-sponsorship-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>Go to the repo settings.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Under Features-Sponsorships, configure the sponsorship link. The configuration will ultimately be stored in the file &lt;code>.github/FUNDING.yml&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-04-25-231758.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="note-1">Note&lt;/h4>&lt;p>As mentioned above, the sponsorship link can be the official sponsor method or an external link like PayPal. Therefore, even if the Sponsor application doesn&amp;rsquo;t work, you can use other techniques, though the official method provides a better experience.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Open-source projects generally have slim profits and require a lot of time. Therefore, if there is a good mechanism to earn some revenue, it can better motivate developers to continue their work, thus contributing to better projects. So, consider doing it.&lt;/p></description></item><item><title>Automatically Append Prefix/Suffix to Storybook Titles</title><link>https://en.1991421.cn/2022/04/23/storybook-title/</link><pubDate>Sat, 23 Apr 2022 11:26:18 +0800</pubDate><guid>https://en.1991421.cn/2022/04/23/storybook-title/</guid><description>&lt;blockquote>
&lt;p>Storybook is convenient for building component library docs. In one business component library, we use Chinese titles for clarity, but component names are in English. We wanted the component name to appear in the sidebar title too, so English search works. Hence the requirement: automatically append the component name to the title.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-04-23-114114.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-04-23-114428.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Manual editing — too repetitive for N components, and renames require manual maintenance. Pass.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Built-in options — Storybook offers &lt;code>titlePrefix&lt;/code>, which adds a unified prefix per folder, but doesn’t meet our needs. Pass.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Build our own — Storybook builds with webpack and the title definition syntax is predictable, so we wrote a webpack loader to process story files uniformly.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="implementation-outline">
&lt;a class="heading-anchor-link" href="#implementation-outline">Implementation Outline&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-outline"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>@babel supports bidirectional conversion between TSX/JSX and AST. So we can read the file, locate and modify the &lt;code>title&lt;/code>, then generate code and continue the build.&lt;/p>
&lt;p>Tools used:&lt;/p>
&lt;ol>
&lt;li>@babel/parser — parser to produce AST&lt;/li>
&lt;li>@babel/traverse — AST traversal utilities&lt;/li>
&lt;li>@babel/generator — generate code from AST&lt;/li>
&lt;li>webpack loader — transform target files and emit new code&lt;/li>
&lt;/ol>
&lt;p>For customization, the loader exposes &lt;code>appendTitle&lt;/code> as a callback, providing the current &lt;code>title&lt;/code> and &lt;code>componentName&lt;/code>, so consumers can define their own logic. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">webpackFinal&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">configType&lt;/span>&lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">rules&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sr">/\.stories\.(jsx?$|tsx?$)/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">use&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;@stacker/storybook-webpack-auto-title-loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">appendTitle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">({&lt;/span>&lt;span class="nx">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">componentName&lt;/span>&lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">title&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">-&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">componentName&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}],&lt;/span> &lt;span class="nx">include&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;../src&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">config&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Plugin: &lt;a href="https://github.com/alanhe421/storybook-webpack-auto-title-loader" target="_blank" rel="noopener">https://github.com/alanhe421/storybook-webpack-auto-title-loader&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With this loader, you can programmatically control Storybook titles.&lt;/p></description></item><item><title>Remote Development IDEs</title><link>https://en.1991421.cn/2022/04/13/remote-development/</link><pubDate>Wed, 13 Apr 2022 23:29:54 +0800</pubDate><guid>https://en.1991421.cn/2022/04/13/remote-development/</guid><description>&lt;blockquote>
&lt;p>Recently, I spent some time trying out VSC and JB&amp;rsquo;s remote development tools. I think they&amp;rsquo;re good, so here’s a brief introduction.&lt;/p>
&lt;/blockquote>
&lt;p>From now on, high-overhead compilation tasks can be handled by remote machines.&lt;/p>
&lt;p>Of course, to ensure smooth remote operation, both the remote machine and the local network need to meet high requirements.&lt;/p>
&lt;h2 id="visual-studio-code">
&lt;a class="heading-anchor-link" href="#visual-studio-code">Visual Studio Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="visual-studio-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-07-15-224300.jpeg"
alt="https://static.1991421.cn/2024/2024-07-15-224300.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>To implement remote development with VSC, you need to follow these steps:&lt;/p>
&lt;ol>
&lt;li>Deploy &lt;a href="https://github.com/coder/code-server" target="_blank" rel="noopener">Code Server&lt;/a>&lt;/li>
&lt;li>Install Visual Studio Code (VSC) locally&lt;/li>
&lt;li>Install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh" target="_blank" rel="noopener">Visual Studio Code Remote - SSH&lt;/a> extension&lt;/li>
&lt;li>Add SSH configuration and connect&lt;/li>
&lt;/ol>
&lt;h2 id="jetbrains-gateway">
&lt;a class="heading-anchor-link" href="#jetbrains-gateway">JetBrains Gateway&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jetbrains-gateway"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://www.jetbrains.com/remote-development/gateway/img/Gateway-Enable-a-remote.png"
alt="https://www.jetbrains.com/remote-development/gateway/img/Gateway-Enable-a-remote.png"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>For JB&amp;rsquo;s remote development, you need to install the local client &lt;code>JetBrains Gateway&lt;/code>&lt;/p>
&lt;ol>
&lt;li>Install JetBrains Gateway locally&lt;/li>
&lt;li>Launch it and enter the server address, port, etc.&lt;/li>
&lt;li>Please select the server IDE to install as needed, for example, I chose &lt;code>WebStorm&lt;/code>&lt;/li>
&lt;li>Choose the project you want to open&lt;/li>
&lt;/ol>
&lt;p>For server-side Git projects, you can open a terminal within the IDE and use command-line operations directly.&lt;/p>
&lt;h3 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Gateway is still in &lt;code>Beta&lt;/code>, and compared to VSC, it is less stable.&lt;/li>
&lt;li>Gateway will read the plugin settings of the local JB account&amp;rsquo;s corresponding IDE and synchronize them to the server IDE, so the experience with hotkeys, etc., remains consistent.&lt;/li>
&lt;/ul>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on my experience, reducing local resource overhead is possible by dropping big projects to the server.&lt;/p></description></item><item><title>Whistle Plugin Development - remote-rules</title><link>https://en.1991421.cn/2022/04/09/whistle/</link><pubDate>Sat, 09 Apr 2022 17:32:46 +0800</pubDate><guid>https://en.1991421.cn/2022/04/09/whistle/</guid><description>&lt;blockquote>
&lt;p>Recently, the company provided a cloud IDE service, allowing some development work to switch from local to remote machines. At the same time, the frontend is moving towards micro-frontends, which always require proxies during development. With the emergence of the cloud IDE, the proxy address will change with the IDE address, which drives the need to solve the problem of loading remote proxy rules with one click.&lt;/p>
&lt;/blockquote>
&lt;p>The overall development process with cloud IDE:&lt;/p>
&lt;ol>
&lt;li>When the task management system issues a story&lt;/li>
&lt;li>The developer creates a feat/fix branch, CI automatically creates a cloud IDE development environment and a new Whistle proxy rules file&lt;/li>
&lt;li>CI sends a message informing the cloud IDE address and the Whistle proxy rules file address&lt;/li>
&lt;li>The developer clicks on the Whistle proxy address to directly load the rules&lt;/li>
&lt;li>The developer starts coding&lt;/li>
&lt;/ol>
&lt;p>The advantage of this is that developers don&amp;rsquo;t have to manually maintain proxy rule files.&lt;/p>
&lt;p>To solve this need, I started Whistle plugin development and found that Whistle provides scaffolding tools and some demos, making the overall development process relatively smooth. However, some issues are not clearly described on the official website, so I will briefly summarize them here, which may help some peers.&lt;/p>
&lt;h2 id="development">
&lt;a class="heading-anchor-link" href="#development">Development&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="development"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The general implementation logic is as follows:&lt;/p>
&lt;ol>
&lt;li>Use the UI Server to expose a new URL for the Whistle web service, supporting the remoteRules parameter&lt;/li>
&lt;li>Handle the parameter in the plugin UI router, download the rule file based on the parameter value, and save it temporarily&lt;/li>
&lt;li>Call the Whistle command &lt;code>w2 add&lt;/code> to load it&lt;/li>
&lt;li>Redirect to the Whistle web rules page&lt;/li>
&lt;/ol>
&lt;p>Plugin installation and source code &lt;a href="https://github.com/alanhe421/whistle.remote-rules" target="_blank" rel="noopener">click here&lt;/a>, which may help some people.&lt;/p>
&lt;h2 id="usage-effect">
&lt;a class="heading-anchor-link" href="#usage-effect">Usage Effect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-effect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ npm i whistle -g
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm i whistle.remote-rules -g
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># or&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ w2 i whistle.remote-rules
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Automatically load remote rules, address template as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">http://local.whistlejs.com/whistle.remote-rules/?remoteRules={url}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">http://local.whistlejs.com/whistle.remote-rules/?remoteRules=https%3A%2F%2Fgist.githubusercontent.com%2Falanhg%2Fa950d216121002d5bb0fd1278789da75%2Fraw%2Fc8bdf23e05c9dc504ea393a0b373cb556df911d8%2Ftest-whistle.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With this plugin support, CI can directly push the above address. After the developer clicks to load it, they can continue development and debugging.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Some aspects of the Whistle official documentation and examples are not very clear, so I will supplement them here.&lt;/p>
&lt;ol>
&lt;li>Plugin debugging&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If Whistle is running, you need to manually stop it&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ w2 stop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Run Whistle in debug mode; exceptions or console prints from the plugin script will be printed in the terminal where the script was started&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ w2 run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Plugin development, hot reload&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Real-time compilation when modifying scripts, needed for TS development&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm run dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Monitor dist and rules files; if there are fixes, the loaded plugin will be automatically updated, and the terminal will print reload information&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ lack watch
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>Publishing&lt;/li>
&lt;/ol>
&lt;p>Plugin publishing does not require pushing to the Whistle author&amp;rsquo;s &lt;a href="https://github.com/avwo/whistle" target="_blank" rel="noopener">repo&lt;/a> or &lt;a href="https://github.com/whistle-plugins" target="_blank" rel="noopener">whistle-plugins&lt;/a> organization. You can publish it under your own name. Just note that Whistle uses package names to ensure recognition, such as &lt;code>whistle.remote-rules&lt;/code> which I developed.&lt;/p>
&lt;ol start="4">
&lt;li>Whistle-server&lt;/li>
&lt;/ol>
&lt;p>Whistle provides multiple servers; choose the corresponding server based on your needs. For example, if you need to expand UI services and implement custom pages to perform certain actions, such as exposing URLs and automatically downloading remote rules as I did here, you should rely on uiServer, and the router corresponds to some APIs or sub-route pages you implement.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Whistle exposes interfaces that support extending proxy features based on personal needs, which is very impressive.&lt;/p></description></item><item><title>Global Type Definition Issues in Third-party Packages</title><link>https://en.1991421.cn/2022/04/05/work/</link><pubDate>Tue, 05 Apr 2022 23:22:46 +0800</pubDate><guid>https://en.1991421.cn/2022/04/05/work/</guid><description>&lt;blockquote>
&lt;p>When developing TS packages under NPM, you might add custom properties to Window or add custom properties to Node Env. These global type definition files are all .d.ts. Even if these files are published in the package, when used in specific projects, you&amp;rsquo;ll find that type hints don&amp;rsquo;t work. For safety, let&amp;rsquo;s solve this.&lt;/p>
&lt;/blockquote>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Solutions differ for TS/JS projects&lt;/p>
&lt;/blockquote>
&lt;h3 id="ts-project">
&lt;a class="heading-anchor-link" href="#ts-project">TS Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ts-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add the definition file to the include section in &lt;code>tsconfig.json&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-04-05-232616.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="js-project">
&lt;a class="heading-anchor-link" href="#js-project">JS Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="js-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-04-05-233016.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: paths only support absolute paths and relative paths; project root paths are not recognized&lt;/p>
&lt;p>After configuring as above, when using related objects, these properties will be available, and clicking can normally jump to the definition file.&lt;/p>
&lt;h2 id="best-approach">
&lt;a class="heading-anchor-link" href="#best-approach">Best Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="best-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The best approach for TS projects is to publish @types packages&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript&amp;rsquo;s only purpose is types, so ensure type hints and type safety as much as possible.&lt;/p>
&lt;h2 id="related-materials">
&lt;a class="heading-anchor-link" href="#related-materials">Related Materials&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-materials"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://stackoverflow.com/questions/59361404/custom-typescript-declaration-file-in-npm-package-not-working" target="_blank" rel="noopener">https://stackoverflow.com/questions/59361404/custom-typescript-declaration-file-in-npm-package-not-working&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://stackoverflow.com/questions/39945274/node-js-reference-path-absolute" target="_blank" rel="noopener">https://stackoverflow.com/questions/39945274/node-js-reference-path-absolute&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>HTTPS Certificate Revocation</title><link>https://en.1991421.cn/2022/03/28/https/</link><pubDate>Mon, 28 Mar 2022 20:10:48 +0800</pubDate><guid>https://en.1991421.cn/2022/03/28/https/</guid><description>&lt;blockquote>
&lt;p>Recently, I came across news that a Western certificate authority refused to issue HTTPS certificates for Russian domains and even revoked already issued valid certificates.&lt;/p>
&lt;p>This made me realize that besides natural expiration, certificates can also be revoked. This was a blind spot for me, so I decided to research and summarize it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="https-certificate-validity-period">
&lt;a class="heading-anchor-link" href="#https-certificate-validity-period">HTTPS Certificate Validity Period&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="https-certificate-validity-period"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, it&amp;rsquo;s important to understand that HTTPS certificates have validity periods and they do expire. This design ensures that website identity information remains current and accurate. Typically, certificates have a maximum validity period of 1 year.&lt;/p>
&lt;h2 id="certificate-revocation-list-crl-mechanism">
&lt;a class="heading-anchor-link" href="#certificate-revocation-list-crl-mechanism">Certificate Revocation List (CRL) Mechanism&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="certificate-revocation-list-crl-mechanism"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The revocation mechanism allows Certificate Authorities (CAs) to revoke certificates that haven&amp;rsquo;t yet expired. After revocation, when users access the site using a browser, they will be directly alerted that the certificate is invalid.&lt;/p>
&lt;p>Regarding the CRL mechanism, here are the key points:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>When establishing a secure connection during the first visit to a site, the certificate returned by the site server contains a CRL check address&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-03-28-211218.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The browser will use the CRL address to check if the certificate is online. If it is, it will continue to check the certificate&amp;rsquo;s validity period, establish a secure connection, and send encrypted information&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The browser stores CRL addresses with a validity period, typically 24 hours. This means that even if a certificate is still within its validity period, if the CA revokes the certificate, the website will report as insecure within at most 24 hours&lt;/p>
&lt;ul>
&lt;li>For example, in Chrome browser, you can visit &lt;code>chrome://components/&lt;/code>, search for CRL, and manually update it&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding HTTPS certificate revocation is crucial for web security. The CRL mechanism provides an important safety net that allows Certificate Authorities to respond quickly to security incidents by revoking compromised certificates before their natural expiration. While modern browsers have largely transitioned to more efficient mechanisms like OCSP (Online Certificate Status Protocol), the fundamental concept of certificate revocation remains a critical component of the HTTPS ecosystem. It&amp;rsquo;s important for developers and system administrators to be aware of these mechanisms to ensure proper certificate management and maintain secure web communications.&lt;/p></description></item><item><title>File Upload Accept Compatibility Issues</title><link>https://en.1991421.cn/2022/03/20/accept/</link><pubDate>Sun, 20 Mar 2022 10:57:46 +0800</pubDate><guid>https://en.1991421.cn/2022/03/20/accept/</guid><description>&lt;blockquote>
&lt;p>Recently received a user-submitted issue where certificate file uploads always failed when selecting files in Ubuntu Firefox. After investigation, it was found to be a compatibility issue, so I&amp;rsquo;m summarizing it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="input-accept">
&lt;a class="heading-anchor-link" href="#input-accept">input-accept&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="input-accept"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, let me explain the meaning of this attribute setting. The accept attribute is used to limit file upload types. The value of accept can be a file extension, such as &lt;code>.jpg&lt;/code>, or a &lt;code>mime&lt;/code> type, such as &lt;code>image/jpeg&lt;/code>. The HTML standard also supports &lt;code>comma-separated multiple file types&lt;/code>.&lt;/p>
&lt;h2 id="problem-code">
&lt;a class="heading-anchor-link" href="#problem-code">Problem Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code> &amp;lt;input type=&amp;quot;file&amp;quot; accept=&amp;quot;application/x-x509-ca-cert&amp;quot; /&amp;gt;&lt;/code>&lt;/p>
&lt;p>Phenomenon: In Ubuntu Firefox, unable to select certificate text files with .cer or .crt extensions. The current project mostly uses encapsulated UI components, meaning upload restrictions at the JS level. Switching to native implementation shows the same problem.&lt;/p>
&lt;h2 id="compatibility-issues">
&lt;a class="heading-anchor-link" href="#compatibility-issues">Compatibility Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on the problem code, I retested in several environments with the following results, which also reproduced the user&amp;rsquo;s issue.&lt;/p>
&lt;ul>
&lt;li>Mac Chrome v99.0.4844.74: Normal&lt;/li>
&lt;li>Mac Firefox v97.0.2: Normal&lt;/li>
&lt;li>Ubuntu Firefox v93.0: Error&lt;/li>
&lt;li>Ubuntu Chrome v99.0.4844.82: Error&lt;/li>
&lt;/ul>
&lt;h2 id="root-cause-analysis">
&lt;a class="heading-anchor-link" href="#root-cause-analysis">Root Cause Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="root-cause-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Cert files are recognized as MIME type &lt;code>application/pkix-cert&lt;/code> on Ubuntu, while on Mac they are &lt;code>application/x-x509-ca-cert&lt;/code>.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Changed to file extension-based restriction: &lt;code> &amp;lt;input type=&amp;quot;file&amp;quot; accept=&amp;quot;.cer,.crt&amp;quot; /&amp;gt;&lt;/code>. Retested and found it works fine.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For MIME-based type restrictions on text files, it&amp;rsquo;s recommended to prioritize file extension approach.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://askubuntu.com/questions/7517/what-is-the-relationship-between-mime-types-and-file-extensions" target="_blank" rel="noopener">https://askubuntu.com/questions/7517/what-is-the-relationship-between-mime-types-and-file-extensions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/file" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/file&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/react-dropzone/attr-accept" target="_blank" rel="noopener">https://github.com/react-dropzone/attr-accept&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Deriving Public Keys from Private Keys in JavaScript</title><link>https://en.1991421.cn/2022/03/06/js-export-public-key-from-private/</link><pubDate>Sun, 06 Mar 2022 12:53:29 +0800</pubDate><guid>https://en.1991421.cn/2022/03/06/js-export-public-key-from-private/</guid><description>&lt;blockquote>
&lt;p>I had a requirement to implement public key derivation from private keys in the frontend. After researching several solutions, I finally solved it. Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use &lt;a href="https://github.com/digitalbazaar/forge" target="_blank" rel="noopener">node-forge&lt;/a>, which supports both browser and Node.js environments, making it suitable for frontend use.&lt;/p>
&lt;p>Here&amp;rsquo;s how to use it in Node.js:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">forge&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;node-forge&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="c1">// For browser usage, replace with import or directly import the JS module via script tag
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">privateKeyPem&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/client1.key`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span> &lt;span class="c1">// client1 is a PEM format private key file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">forgePriKey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">forge&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pki&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">privateKeyFromPem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">privateKeyPem&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">forgePubKey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">forge&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pki&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setRsaPublicKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">forgePriKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">forgePriKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">publicKeyPem&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">forge&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pki&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">publicKeyToPem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">forgePubKey&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="other-encryption-approaches">
&lt;a class="heading-anchor-link" href="#other-encryption-approaches">Other Encryption Approaches&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-encryption-approaches"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The native frontend &lt;code>crypto&lt;/code> API currently has limited functionality and cannot solve this problem&lt;/li>
&lt;li>The commonly used community encryption library &lt;code>jsrsasign&lt;/code> currently doesn&amp;rsquo;t support deriving public keys from private keys&lt;/li>
&lt;li>The Node.js Crypto module supports &lt;code>crypto.createPublicKey(key)&lt;/code>, but it can only be used on the server side&lt;/li>
&lt;li>The command-line tool &lt;code>openssl&lt;/code> supports &lt;code>openssl pkey -in ./client1.key -pubout&lt;/code>, but it can only be used on the server side&lt;/li>
&lt;/ol>
&lt;p>In summary, if you must implement this in the frontend, you need to use node-forge. If you can leverage server-side implementation, then any of the above solutions will work.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I believe there are two main reasons why frontend encryption was historically weak: 1) Limited use cases - the web focused more on interaction than security; 2) Significant performance overhead for encryption/decryption operations. However, with the current development of web technologies, various application scenarios continue to emerge, and hardware improvements have made performance overhead less of an issue. As a result, frontend encryption requirements are becoming increasingly common, putting frontend encryption on the development agenda, as evidenced by the emergence of crypto modules.&lt;/p></description></item><item><title>Refs in React</title><link>https://en.1991421.cn/2022/03/01/reactref/</link><pubDate>Tue, 01 Mar 2022 22:40:42 +0800</pubDate><guid>https://en.1991421.cn/2022/03/01/reactref/</guid><description>&lt;blockquote>
&lt;p>Refs are quite commonly used in React - they enable parent components to manipulate child components/native DOM, such as controlling a modal component popup display, etc. But sometimes you can encounter pitfalls when using them that are easy to overlook. Let&amp;rsquo;s organize these here.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>After ref successfully mounts an element, it does not trigger component re-rendering&lt;/p>
&lt;p>Sometimes you might add ref or ref.current to useEffect dependencies, but actually when the component referenced by the ref is mounted, although ref.current already holds the instance reference, the component won&amp;rsquo;t trigger re-rendering, let alone the effect re-executing. Therefore, when encountering such requirements, you need to modify the solution. Methods are as follows:&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For ref usage, you can directly assign ref objects, or use callbacks. Both methods serve the same purpose, with the only difference being that if you want to perform some actions when mounting, you can use callbacks. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-react" data-lang="react">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">App&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">pElRef&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useRef&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">any&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">refresh&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">setRefresh&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">useEffect&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;render?&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">refresh&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">onAttached&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useCallback&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">ref&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">refresh&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setRefresh&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">refresh&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pElRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">refresh&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App-header&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">logo&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App-logo&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;logo&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">ref&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">onAttached&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Edit&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">code&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nx">App&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tsx&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">code&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">and&lt;/span> &lt;span class="nx">save&lt;/span> &lt;span class="nx">to&lt;/span> &lt;span class="nx">reload&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App-link&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://reactjs.org&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">target&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;_blank&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;noopener noreferrer&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Learn&lt;/span> &lt;span class="nx">React&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>​ As shown above, add a state parameter refresh and modify it when mounting succeeds to achieve re-rendering.&lt;/p>
&lt;h2 id="ref-forwarding-in-hoc">
&lt;a class="heading-anchor-link" href="#ref-forwarding-in-hoc">Ref Forwarding in HOC&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ref-forwarding-in-hoc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When using components, there are several special parameters, such as key and ref. They don&amp;rsquo;t belong to props - React handles them specially, so you need to be careful in HOC scenarios.&lt;/p>
&lt;p>Here&amp;rsquo;s an example: in the following code, the App component hopes to use SayRef to manipulate the User component, but the User component isn&amp;rsquo;t directly provided to App for consumption - it&amp;rsquo;s wrapped by HOC. Here, when props are passed to User, ref won&amp;rsquo;t be forwarded. The ref reference will point to SayHoc, so clicking here won&amp;rsquo;t work.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">SayHoc&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">boss&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">boss&lt;/span>: &lt;span class="kt">string&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">from&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">boss&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="nx">say&lt;/span> &lt;span class="nx">hello&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="na">...props&lt;/span>&lt;span class="p">}/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forwardRef&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">useImperativeHandle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ref&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">say&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&amp;gt;{&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">}&amp;lt;/&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Say&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">SayHoc&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">boss&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;L&amp;#39;&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">App() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">sayRef&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">useRef&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">any&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">header&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App-header&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">logo&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;App-logo&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;logo&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Say&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;2121&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">ref&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">sayRef&lt;/span>&lt;span class="p">}/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sayRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">say&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">say&lt;/span> &lt;span class="nx">alert&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">header&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">App&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The solution is as follows, using React.forwardRef.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">SayHoc&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">boss&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">boss&lt;/span>: &lt;span class="kt">string&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forwardRef&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">props&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ref&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">from&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">boss&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="nx">say&lt;/span> &lt;span class="nx">hello&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="na">ref&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">ref&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="na">...props&lt;/span>&lt;span class="p">}/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/57204134/difference-between-assigning-react-ref-using-a-callback-versus-setting-it-direct" target="_blank" rel="noopener">https://stackoverflow.com/questions/57204134/difference-between-assigning-react-ref-using-a-callback-versus-setting-it-direct&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://reactjs.org/docs/refs-and-the-dom.html" target="_blank" rel="noopener">https://reactjs.org/docs/refs-and-the-dom.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Component Library Docs with Storybook</title><link>https://en.1991421.cn/2022/02/25/storybook/</link><pubDate>Fri, 25 Feb 2022 22:33:20 +0800</pubDate><guid>https://en.1991421.cn/2022/02/25/storybook/</guid><description>&lt;blockquote>
&lt;p>I’ve been building a component library. To promote adoption, I set up docs. After comparing options, I chose &lt;a href="https://github.com/storybookjs/storybook" target="_blank" rel="noopener">Storybook&lt;/a> — popular and fits the needs. The framework is simple but there were some gotchas; notes below.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-12-12-224131.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Initialize&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npx sb init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Hot startup, web browsing available&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm run storybook
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;!-- more -->
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>sb init is not made for empty projects&lt;/code> — run init at the component library root (not an empty folder). Storybook merges its config into your existing project.&lt;/p>
&lt;!-- more -->
&lt;h2 id="details">
&lt;a class="heading-anchor-link" href="#details">Details&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="details"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After initialization, you can view the docs. Next steps are custom configuration and writing example stories.&lt;/p>
&lt;h3 id="project-files">
&lt;a class="heading-anchor-link" href="#project-files">Project files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Key files in the docs setup:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>The .storybook folder contains documentation-related configuration&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;code>main.js&lt;/code>&lt;/p>
&lt;p>Main configuration, required for both packaging and preview&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>preview.js&lt;/code>&lt;/p>
&lt;p>Preview-related configuration&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>Files ending with &lt;code>.stories.tsx&lt;/code> are the docs stories. You can also use &lt;a href="https://storybook.js.org/docs/react/api/mdx" target="_blank" rel="noopener">MDX&lt;/a>.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="component-storiestsx">
&lt;a class="heading-anchor-link" href="#component-storiestsx">Component stories.tsx&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-storiestsx"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>Use &lt;code>/&lt;/code> in &lt;code>title&lt;/code> to create nested menus (2+ levels).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Two ways to document components:&lt;/p>
&lt;ul>
&lt;li>Add multi‑line comments above the component class/function.&lt;/li>
&lt;li>Add multi‑line comments to props/fields.&lt;/li>
&lt;/ul>
&lt;p>Use multi‑line comment syntax for both.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Importing global CSS&lt;/li>
&lt;/ol>
&lt;p>If components depend on global styles, import them in &lt;code>.storybook/preview.js&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="s1">&amp;#39;tea-component/dist/tea.css&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Exclude stories from package builds&lt;/li>
&lt;/ol>
&lt;p>Source and docs live together; exclude story files from the library’s tsconfig used for packaging.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;exclude&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;node_modules&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;test/**/*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;lib/**/*.stories.tsx&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For the root tsconfig used by the app, still include stories to keep type safety.&lt;/p>
&lt;ol start="3">
&lt;li>
&lt;p>Global i18n init&lt;/p>
&lt;p>For global initialization (like i18n), run it in &lt;code>.storybook/preview.js&lt;/code>, similar to global CSS.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">translation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Initialize internationalization entries
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">i18n&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">init&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">translation&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Parsing error: &amp;ldquo;parserOptions.project&amp;rdquo; has been set for @typescript-eslint/parser.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>Parsing error: &amp;ldquo;parserOptions.project&amp;rdquo; has been set for @typescript-eslint/parser.
The file does not match your project config: lib/button.stories.tsx.
The file must be included in at least one of the projects provided.&lt;/p>
&lt;/blockquote>
&lt;p>This occurs when ESLint points to a tsconfig that doesn’t include the current file. Fix by updating &lt;code>include&lt;/code> to cover story files.&lt;/p>
&lt;ol start="5">
&lt;li>
&lt;p>Default export of the module has or is using private name &amp;lsquo;HeaderProps&amp;rsquo;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Prop description not hot‑updating?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>When editing prop comments, descriptions may not refresh immediately.&lt;/p>
&lt;/blockquote>
&lt;h2 id="extension">
&lt;a class="heading-anchor-link" href="#extension">Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/storybookjs/storybook/issues/6364" target="_blank" rel="noopener">https://github.com/storybookjs/storybook/issues/6364&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/storybookjs/storybook/issues/10335" target="_blank" rel="noopener">https://github.com/storybookjs/storybook/issues/10335&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Synology Chrome Downloader Only Accepts HTTPS</title><link>https://en.1991421.cn/2022/02/19/https/</link><pubDate>Sat, 19 Feb 2022 17:37:57 +0800</pubDate><guid>https://en.1991421.cn/2022/02/19/https/</guid><description>&lt;blockquote>
&lt;p>After an update, the &lt;code>NAS Download Manager (for Synology)&lt;/code> Chrome extension only accepted HTTPS URLs, but my NAS download service was still HTTP-only. Instead of reconfiguring the NAS, I solved it client-side.&lt;/p>
&lt;/blockquote>
&lt;h2 id="solution-whistle">
&lt;a class="heading-anchor-link" href="#solution-whistle">Solution: whistle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution-whistle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>whistle&lt;/code> can proxy HTTPS traffic and issue its own certificates, which lets us downgrade the request.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Configure whistle and your Chrome proxy extension so requests are intercepted. Follow the &lt;a href="https://wproxy.org/whistle/install.html" target="_blank" rel="noopener">official docs&lt;/a>:&lt;/p>
&lt;ol>
&lt;li>Enable HTTPS in whistle.&lt;/li>
&lt;li>Install and trust the root CA certificate.&lt;/li>
&lt;li>Use an extension such as &lt;a href="https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif" target="_blank" rel="noopener">SwitchyOmega&lt;/a> so Chrome goes through the whistle proxy.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>
&lt;p>Add a rule that rewrites HTTPS requests to HTTP, so whistle forwards them using plain HTTP:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># NAS service endpoint
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/https(://110.100.176.222:50098.+)/ http$1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>With that rule active, paste the URL into the download extension and it will work.&lt;/p>
&lt;img src="https://static.1991421.cn/2022/2022-02-19-181317.jpeg" style="zoom:50%;" />
&lt;!-- more -->
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You could also enable HTTPS directly on the NAS. This post focuses on the client-side workaround via proxying.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://chrome.google.com/webstore/detail/nas-download-manager-for/iaijiochiiocodhamehbpmdlobhgghgi" target="_blank" rel="noopener">NAS Download Manager (for Synology)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://wproxy.org/whistle/install.html" target="_blank" rel="noopener">whistle documentation&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>nginx njs Upload Implementation</title><link>https://en.1991421.cn/2022/02/17/nginx-njs/</link><pubDate>Thu, 17 Feb 2022 12:52:32 +0800</pubDate><guid>https://en.1991421.cn/2022/02/17/nginx-njs/</guid><description>&lt;blockquote>
&lt;p>Recently had nginx requirement to implement upload feature, pleasantly discovered it already supports js, so trying to use js to implement upload.&lt;/p>
&lt;/blockquote>
&lt;h2 id="nginx-configuration">
&lt;a class="heading-anchor-link" href="#nginx-configuration">nginx Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nginx-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>To support njs need module loading&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For specific js business module usage, need to use js related directives&lt;/p>
&lt;/li>
&lt;li>
&lt;p>load_module directive needs to be placed globally in &lt;code>default.conf&lt;/code>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Example as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">load_module&lt;/span> &lt;span class="n">modules&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ngx_http_js_module&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">so&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">events&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">http&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># No longer restricting here, nginx default is 1MB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">client_max_body_size&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">js_path&lt;/span> &lt;span class="s2">&amp;#34;/etc/nginx/njs/&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">js_import&lt;/span> &lt;span class="n">main&lt;/span> &lt;span class="n">from&lt;/span> &lt;span class="n">upload&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">location&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">upload&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cert&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">js_content&lt;/span> &lt;span class="n">main&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">resolve&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="njs-module-business-code">
&lt;a class="heading-anchor-link" href="#njs-module-business-code">njs Module Business Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="njs-module-business-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>njs is just js subset, so syntax and functionality are also limited&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">fs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;fs&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">crypto&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;crypto&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">dest&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`/var/www/ssl`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">writeFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fileContent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fileSuffix&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">fileName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">crypto&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createHash&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;md5&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">update&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fileContent&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">digest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;hex&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">.&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">fileSuffix&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="s1">&amp;#39;crt&amp;#39;&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">dest&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">fileName&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fileContent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">fileName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">r&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">body&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">r&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">requestBody&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">r&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">crtFileName&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">writeFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">crtFile&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">keyFileName&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">writeFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">keyFile&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;key&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">r&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">upload&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">r&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Storybook Build Error</title><link>https://en.1991421.cn/2022/02/14/storybook/</link><pubDate>Mon, 14 Feb 2022 20:12:29 +0800</pubDate><guid>https://en.1991421.cn/2022/02/14/storybook/</guid><description>&lt;p>Our team uses Storybook for a UI component library and docs. In CI, we hit the following error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">vendors&lt;/span>&lt;span class="o">~&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">54994&lt;/span>&lt;span class="n">c12&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">iframe&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bundle&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="n">Unexpected&lt;/span> &lt;span class="n">error&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="n">loading&lt;/span> &lt;span class="o">./&lt;/span>&lt;span class="n">button&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stories&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tsx&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Module&lt;/span> &lt;span class="n">parse&lt;/span> &lt;span class="n">failed&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Unexpected&lt;/span> &lt;span class="n">token&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="ne">File&lt;/span> &lt;span class="n">was&lt;/span> &lt;span class="n">processed&lt;/span> &lt;span class="n">with&lt;/span> &lt;span class="n">these&lt;/span> &lt;span class="n">loaders&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span> &lt;span class="o">../../../&lt;/span>&lt;span class="n">cache&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">node_modules&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">storybook&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">source&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">loader&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">cjs&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">You&lt;/span> &lt;span class="n">may&lt;/span> &lt;span class="n">need&lt;/span> &lt;span class="n">an&lt;/span> &lt;span class="n">additional&lt;/span> &lt;span class="n">loader&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">handle&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="n">of&lt;/span> &lt;span class="n">these&lt;/span> &lt;span class="n">loaders&lt;/span>&lt;span class="o">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="n">backgroundColor&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">control&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;color&amp;#39;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">|&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="n">as&lt;/span> &lt;span class="n">ComponentMeta&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nb">typeof&lt;/span> &lt;span class="ne">Button&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">More&lt;/span> &lt;span class="n">on&lt;/span> &lt;span class="n">component&lt;/span> &lt;span class="n">templates&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">https&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="n">storybook&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">docs&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">react&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">writing&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">stories&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">introduction&lt;/span>&lt;span class="c1">#using-args&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Looking through the official repo, a related issue/PR suggested upgrading Storybook packages: &lt;code>npx sb upgrade&lt;/code>.&lt;/p>
&lt;p>After upgrading, the problem was resolved.&lt;/p>
&lt;p>As for the root cause, the official PR explains it: an incorrect TypeScript configuration/loader order in webpack — TS compilation needs to run before other processing.&lt;/p></description></item><item><title>Pulling Images from a Private Docker Registry</title><link>https://en.1991421.cn/2022/02/08/docker/</link><pubDate>Tue, 08 Feb 2022 21:55:49 +0800</pubDate><guid>https://en.1991421.cn/2022/02/08/docker/</guid><description>&lt;blockquote>
&lt;p>When pulling images from a private registry on a remote office PC, I got a &amp;ldquo;Service Available&amp;rdquo; error. After investigation, the following steps fixed it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Disable the Docker App proxy.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Add a hosts entry locally, for example &lt;code>9.111.111.111 xxx.1991421.cn&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Edit &lt;code>~/.docker/config.json&lt;/code> and add hosts.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;credsStore&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;desktop&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;auths&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hosts&amp;#34;&lt;/span>&lt;span class="p">:[&lt;/span>&lt;span class="s2">&amp;#34;tcp://127.0.0.1:12639&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol></description></item><item><title>Alpha in CSS</title><link>https://en.1991421.cn/2022/02/07/cssalpha/</link><pubDate>Mon, 07 Feb 2022 10:27:07 +0800</pubDate><guid>https://en.1991421.cn/2022/02/07/cssalpha/</guid><description>&lt;blockquote>
&lt;p>There are three places in CSS related to transparency settings: Alpha in RGBA, transparent value in color settings, and the Opacity property.&lt;/p>
&lt;p>I don&amp;rsquo;t write styles often, which leads to confusion about these basic concepts. Here I&amp;rsquo;m organizing and marking them down.&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-meaning-of-the-word-alpha">
&lt;a class="heading-anchor-link" href="#the-meaning-of-the-word-alpha">The Meaning of the Word Alpha&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-meaning-of-the-word-alpha"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Alpha Channel - Specifies the opacity of an object.&lt;/p>
&lt;/blockquote>
&lt;p>I used to think this represented transparency, opposite to opacity. Of course, this understanding is wrong. The reason I was misled is partly because the explanation of this word in some dictionaries is misleading. Below is part of the explanation from Eudic Dictionary when looking up the word.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-02-07-103948.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Unconsciously, one would think that alpha-related settings would be transparency settings, but this is completely wrong.&lt;/p>
&lt;!-- more -->
&lt;p>Looking at authoritative dictionaries like Oxford, the meaning is only &lt;code>the first letter of the Greek alphabet&lt;/code>. So why is it related to transparency? I checked the meaning of Alpha channel in WIKI and finally figured it out.&lt;/p>
&lt;p>As the Oxford dictionary says, Alpha means the first letter. The person who invented the Alpha channel named it alpha because it was the first channel beyond RGB colors, hence named Alpha. The function of the Alpha channel is image composition. For example, originally it was 255 red, but now the display needs to be multiplied by this alpha value, thus creating a transparency effect. However, understanding it through this formula is actually opacity. So if the alpha value is 0, it&amp;rsquo;s completely transparent; conversely, if the value is 1, it will be fully displayed.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Understanding the above context helps understand one viewpoint online: alpha is not transparency/opacity, it just ultimately creates a transparency effect.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>But in terms of the function of opacity, alpha in CSS is similar to opacity&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="alphatransparentopacity">
&lt;a class="heading-anchor-link" href="#alphatransparentopacity">alpha/transparent/opacity&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="alphatransparentopacity"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>alpha exists as a value in the RGBA function, and RGBA currently only exists in background-color. Therefore, this opacity setting only serves the background color, and it&amp;rsquo;s important to know that the opacity of the background color, i.e., the Alpha value, is not inherited.&lt;/li>
&lt;li>transparent exists in color property settings, its function is equivalent to &lt;code>rgba(0,0,0,0)&lt;/code>, and the effect will be transparent.&lt;/li>
&lt;li>opacity is a separate property setting, meaning the opacity of the entire container, including background/text&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>So, CSS can&amp;rsquo;t be called difficult, but there&amp;rsquo;s still a lot to learn. Continuing to study.&lt;/p></description></item><item><title>Composition Events in JavaScript</title><link>https://en.1991421.cn/2022/02/03/jscomposition-event/</link><pubDate>Thu, 03 Feb 2022 12:50:30 +0800</pubDate><guid>https://en.1991421.cn/2022/02/03/jscomposition-event/</guid><description>&lt;blockquote>
&lt;p>I often use Vimium in Chrome to search bookmarks. Some bookmarks are Chinese and some are English, so I need to switch input methods. I noticed Vimium searches in real time based on input characters, regardless of whether an IME is in use. I hadn&amp;rsquo;t encountered this in development, so I decided to understand it properly.&lt;/p>
&lt;/blockquote>
&lt;h2 id="can-js-get-the-current-input-method-name-or-temporarily-switch-input-method">
&lt;a class="heading-anchor-link" href="#can-js-get-the-current-input-method-name-or-temporarily-switch-input-method">Can JS get the current input method name or temporarily switch input method?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="can-js-get-the-current-input-method-name-or-temporarily-switch-input-method"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;strong>The answer is no for both.&lt;/strong> JS cannot access input method information, nor can it change it. Some pages can prompt whether you&amp;rsquo;re in Chinese/English input mode by observing input events and characters.&lt;/p>
&lt;p>What about Chrome extensions like Vimium? Besides JS, extensions have some Chrome App capabilities, but they still cannot modify input methods.&lt;/p>
&lt;h2 id="input-events-in-js-input-and-composition">
&lt;a class="heading-anchor-link" href="#input-events-in-js-input-and-composition">Input events in JS: input and composition&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="input-events-in-js-input-and-composition"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In JS there are these events:&lt;/p>
&lt;ul>
&lt;li>input&lt;/li>
&lt;li>composition
&lt;ul>
&lt;li>compositionstart / compositionupdate / compositionend&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Whenever a user inputs something, the input event fires. Even with a Chinese IME, input fires in real time.&lt;/p>
&lt;p>Composition events only fire when using IMEs or other multi-key character input. They trigger compositionstart, compositionupdate, and compositionend in sequence.&lt;/p>
&lt;p>Vimium listens to input events, which is why it keeps searching continuously.&lt;/p>
&lt;p>Note: InputEvent has an &lt;code>isComposing&lt;/code> property, which tells you whether you are currently composing (e.g., in a Chinese IME). This is very useful.&lt;/p>
&lt;h2 id="my-understanding">
&lt;a class="heading-anchor-link" href="#my-understanding">My understanding&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="my-understanding"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Input can be direct or indirect. For English words, keyboard input is the final output. For Chinese, you type &lt;code>weixin&lt;/code> and then the IME converts it to &lt;code>wechat&lt;/code>. That is indirect/composed input. This explains why JS has composition events.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-02-03-141718.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>How to Use Web Workers (Step-by-Step Guide)</title><link>https://en.1991421.cn/2022/01/31/web-worker/</link><pubDate>Mon, 31 Jan 2022 23:47:38 +0800</pubDate><guid>https://en.1991421.cn/2022/01/31/web-worker/</guid><description>&lt;blockquote>
&lt;p>JavaScript runs in a single thread. If the frontend has blocking computations, the UI can feel sluggish. To improve the experience, you can offload that work to a Web Worker. You could also move the work to the backend; which to choose depends on the scenario and scalability. I recently hit this in a web project and decided to solve it on the frontend—here are my notes on using Web Workers.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-02-01-000118.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;!-- more -->
&lt;h2 id="setup">
&lt;a class="heading-anchor-link" href="#setup">Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="tsconfigjson">
&lt;a class="heading-anchor-link" href="#tsconfigjson">tsconfig.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tsconfigjson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add Web Worker type declarations.&lt;/p>
&lt;p>tsconfig.json&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;lib&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;webworker&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;dom&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;dom.iterable&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;esnext&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="worker-plugin-configuration">
&lt;a class="heading-anchor-link" href="#worker-plugin-configuration">worker-plugin configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="worker-plugin-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To bundle/load worker modules, configure the plugin.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">WorkerPlugin&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;worker-plugin&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nx">WorkerPlugin&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="import-the-worker-in-app-code">
&lt;a class="heading-anchor-link" href="#import-the-worker-in-app-code">Import the worker in app code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="import-the-worker-in-app-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">myWorker&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Worker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./worker&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;module&amp;#39;&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">myWorker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">postMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">detail&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">myWorker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onmessage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">WebWorkerResponse&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="worker-implementation">
&lt;a class="heading-anchor-link" href="#worker-implementation">Worker implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="worker-implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nx">self&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">DedicatedWorkerGlobalScope&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">onmessage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">req&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">WebWorkerRequest&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">postMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="current-issues">
&lt;a class="heading-anchor-link" href="#current-issues">Current Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Debugging isn’t set up yet; for now, console logging is the fallback.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s the basic usage of Web Workers.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/GoogleChromeLabs/worker-plugin" target="_blank" rel="noopener">https://github.com/GoogleChromeLabs/worker-plugin&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use react-window (Step-by-Step Guide)</title><link>https://en.1991421.cn/2022/01/30/react-window/</link><pubDate>Sun, 30 Jan 2022 16:57:48 +0800</pubDate><guid>https://en.1991421.cn/2022/01/30/react-window/</guid><description>&lt;blockquote>
&lt;p>When dealing with massive list data, rendering all DOM elements at once will inevitably degrade web performance. To optimize this, virtual lists can be used. The principle is that the program still maintains the complete list, but actually only renders a portion. As scrolling occurs, the program dynamically switches the rendered portion. This approach is fast enough and pre-renders additional content, making it virtually imperceptible to users. In the React ecosystem, you can choose to use &lt;code>react-window&lt;/code>. Here&amp;rsquo;s how to use it.&lt;/p>
&lt;/blockquote>
&lt;!-- more -->
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s the code directly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">FixedSizeList&lt;/span> &lt;span class="na">height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">430&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">itemCount&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">itemSize&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;100%&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;txlogs-vtable&amp;#39;&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Row&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">FixedSizeList&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tsx" data-lang="tsx">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">Row&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">logs&lt;/span>: &lt;span class="kt">TxLog&lt;/span>&lt;span class="p">[])&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">memo&lt;/span>&lt;span class="p">(({&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">style&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">index&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">style&lt;/span>: &lt;span class="kt">CSSProperties&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">item&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;txlogs-item&amp;#39;&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;flex meta-layout&amp;#39;&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;datetime&amp;#39;&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">formatDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">timestamp&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above is a basic usage example. For complete parameter support, refer to the official documentation or the repo&amp;rsquo;s d.ts definition file.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>For vertical scrolling, the height of each row must be fixed.&lt;/li>
&lt;li>For each row, &lt;code>the style parameter must be passed to the row component&lt;/code>, otherwise scrolling will have errors/flickering. This is because the style uses absolute positioning to determine the position of each row element within the entire scroll container.&lt;/li>
&lt;/ol>
&lt;h2 id="extensions">
&lt;a class="heading-anchor-link" href="#extensions">Extensions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extensions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>For example, the antd-react component library&amp;rsquo;s table component uses react-window for virtual list support.&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For displaying large datasets, infinite scrolling is one solution. Actually, adjustments can be made in functional design to avoid this, such as pagination. So, technology just serves product design - choose solutions flexibly.&lt;/p></description></item><item><title>Add Global Constants to a Frontend Project</title><link>https://en.1991421.cn/2022/01/25/frontend-global-constants/</link><pubDate>Tue, 25 Jan 2022 21:06:59 +0800</pubDate><guid>https://en.1991421.cn/2022/01/25/frontend-global-constants/</guid><description>&lt;blockquote>
&lt;p>Projects often need settings: some feature‑gating, some just for display (e.g., site name). You can hardcode in TS/JS or keep them in config files (e.g., a custom file or package.json). For the latter, here’s a clean approach.&lt;/p>
&lt;/blockquote>
&lt;!-- more -->
&lt;h2 id="approach">
&lt;a class="heading-anchor-link" href="#approach">Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>package.json中增加自定义字段配置项&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;chainConfig&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sdkVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;v2.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;subscribeMinSdkVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;v2.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: avoid leading underscores and reserved keys for custom fields.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>webpack中读取package.json中字段,利用&lt;em>DefinePlugin&lt;/em>将字段打包进前端构建项目中&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">PACKAGE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;../package.json&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nx">webpack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">DefinePlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;CHAIN_CONFIG&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">PACKAGE&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chainConfig&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Consume in source as a variable; for TS, add a type definition for safety&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">CHAIN_CONFIG&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sdkVersion&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// typing.d.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @description 具体配置值见Package.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">declare&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">CHAIN_CONFIG&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sdkVersion&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">subscribeMinSdkVersion&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>​&lt;/p>
&lt;p>This wires up constants cleanly, but there’s a subtle pitfall — read on.&lt;/p>
&lt;h2 id="webpackdefineplugin">
&lt;a class="heading-anchor-link" href="#webpackdefineplugin">webpack.DefinePlugin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpackdefineplugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Uncaught SyntaxError: Missing } in template expression&lt;/p>
&lt;/blockquote>
&lt;p>When first defining a variable with DefinePlugin, I wrote the following — it threw at runtime:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">new webpack.DefinePlugin({
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#39;CHAIN_CONFIG&amp;#39;: PACKAGE.chainConfig
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}),
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>查看文档，发现变量值的解析，规则如下&lt;/p>
&lt;blockquote>
&lt;p>Each key passed into &lt;code>DefinePlugin&lt;/code> is an identifier or multiple identifiers joined with &lt;code>.&lt;/code>.&lt;/p>
&lt;ul>
&lt;li>If the value is a string it will be used as a code fragment.&lt;/li>
&lt;li>If the value isn&amp;rsquo;t a string, it will be stringified (including functions).&lt;/li>
&lt;li>If the value is an object all keys are defined the same way.&lt;/li>
&lt;li>If you prefix &lt;code>typeof&lt;/code> to the key, it&amp;rsquo;s only defined for typeof calls.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>Because the value is an object, its string properties are treated as code fragments, causing syntax errors. Wrap the object with JSON.stringify so it’s injected as a stringified literal; you can then use it as an object in code.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Keep config in one place (e.g., package.json), inject via DefinePlugin, and type it for TS. Remember to stringify objects when defining constants.&lt;/p></description></item><item><title>Upgrade Blog Theme</title><link>https://en.1991421.cn/2021/12/11/upgrade-blog-theme/</link><pubDate>Sat, 11 Dec 2021 16:27:16 +0800</pubDate><guid>https://en.1991421.cn/2021/12/11/upgrade-blog-theme/</guid><description>&lt;blockquote>
&lt;p>I stayed on Hexo 3.x for a long time because upgrading theme‑next was a hassle. RSS errors turned out to be version‑related, so I bit the bullet and upgraded to simplify future maintenance.&lt;/p>
&lt;/blockquote>
&lt;p>Here&amp;rsquo;s a brief description of the upgrade process:&lt;/p>
&lt;!-- more -->
&lt;h2 id="upgrade-hexonext">
&lt;a class="heading-anchor-link" href="#upgrade-hexonext">Upgrade Hexo/NexT&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrade-hexonext"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Delete the next folder, then download the new theme&lt;/p>
&lt;p>&lt;code>git clone https://github.com/theme-next/hexo-theme-next themes/next&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>New NexT supports external config/custom styles outside the theme dir, making upgrades easier. Create &lt;code>next.yml&lt;/code> and &lt;code>styles.styl&lt;/code> in the project for customization.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2023/2023-06-03-133549.jpg"
alt="https://static.1991421.cn/2023/2023-06-03-133549.jpg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The new theme supports the &lt;code>Gitalk&lt;/code> comment system, so I switched from Disqus, which will improve loading speed and visitor experience.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="gitalk-integration">
&lt;a class="heading-anchor-link" href="#gitalk-integration">Gitalk integration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gitalk-integration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Integration is simple:&lt;/p>
&lt;ol>
&lt;li>Visit &lt;a href="https://github.com/settings/applications/new" target="_blank" rel="noopener">GitHub&lt;/a> to create an application&lt;/li>
&lt;li>Obtain the Client ID and Client Secret, then fill in these details along with the repository name and GitHub account in the configuration file
&lt;ul>
&lt;li>I pointed the repository to my blog for easier comment management&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Fill in the address/callback address with the online address&lt;/li>
&lt;li>The admin_user configuration allows administrators to directly create issues for the corresponding page when they visit&lt;/li>
&lt;li>The script will automatically create an issue and add the label &lt;code>gitalk&lt;/code> and the ID&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Currently, the new version of hexo+theme seems to improve build efficiency&lt;/li>
&lt;li>The new configuration scheme supports custom folders, making future upgrades and maintenance easier&lt;/li>
&lt;/ol></description></item><item><title>Bulk-Replacing Domains in a Frontend Codebase</title><link>https://en.1991421.cn/2021/12/05/frontend-batch-domain-replace/</link><pubDate>Sun, 05 Dec 2021 21:37:31 +0800</pubDate><guid>https://en.1991421.cn/2021/12/05/frontend-batch-domain-replace/</guid><description>&lt;blockquote>
&lt;p>Recently, the product I&amp;rsquo;ve been involved in developing has both domestic and international versions, but they use the same codebase. The main difference between the international and domestic versions, apart from some specific business features that are missing, is the distinction in link addresses.&lt;/p>
&lt;p>For maintainability, we still write domestic site links in the code. If an international user accesses the site and clicks on a link, it will automatically 302 redirect to the international domain, which is fast enough and provides a good experience.&lt;/p>
&lt;p>However, due to various reasons, the automatic redirect has been disabled recently. To maintain a single codebase that works for both international and domestic sites, we need to update the scattered domestic site links in the project and replace them with variable notation instead of fixed domains. For example, replacing &lt;code>https://1991421.cn/2021/09/21/844e39cc/&lt;/code> with &lt;code>https://${window.basicHost}/2021/09/21/844e39cc/&lt;/code>. How can we solve this problem elegantly?&lt;/p>
&lt;/blockquote>
&lt;!-- more -->
&lt;h2 id="direct-regex-replacement">
&lt;a class="heading-anchor-link" href="#direct-regex-replacement">&lt;del>Direct regex replacement?&lt;/del>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="direct-regex-replacement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, the idea of using direct regex replacement comes to mind, but after trying it, I found that it doesn&amp;rsquo;t work. The reason is that there are too many ways to write code under ES6+, and this problem essentially involves changing constants to variable concatenation, so direct text replacement is not feasible.&lt;/p>
&lt;p>Let&amp;rsquo;s list the different context writing methods for links in the code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">href=&amp;#34;https://1991421.cn/2021/09/21/844e39cc/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">href=&amp;#34;//1991421.cn/2021/09/21/844e39cc/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">href={`//1991421.cn/2021/09/21/${postId}`}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">getUrl(`//1991421.cn/2021/09/21/${postId}`)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="ast--regex-replacement">
&lt;a class="heading-anchor-link" href="#ast--regex-replacement">AST + regex replacement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ast--regex-replacement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The direct reason why direct regex replacement doesn&amp;rsquo;t work is the different writing styles. However, after compiling the code to ES5, the writing style becomes fixed as a separate string, such as &lt;code>&amp;quot;//1991421.cn/2021/09/21/&amp;quot; + postId&lt;/code>. This time point can be easily found in webpack, such as &lt;code>compilation.hooks.optimizeChunkAssets&lt;/code>.&lt;/p>
&lt;p>In theory, regex can be used for string replacement at this stage. However, replacement also needs to solve the problem of obtaining the symbol of the string to concatenate and add JS object variables. This is more complicated with pure regex, but much simpler with AST because AST analyzes JS code from a grammatical perspective, directly finds string variables, and simultaneously obtains symbols. Of course, if the idea is feasible, then we need to write a webpack plugin.&lt;/p>
&lt;h2 id="practice">
&lt;a class="heading-anchor-link" href="#practice">Practice&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="webpack-plugin">
&lt;a class="heading-anchor-link" href="#webpack-plugin">Webpack plugin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack-plugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since the ultimate goal is to parse JS code into an AST and then use regex to replace some strings, a webpack plugin is the most suitable approach.&lt;/p>
&lt;p>The specific plugin can be downloaded &lt;a href="https://github.com/alanhe421/webpack-replace-plugin" target="_blank" rel="noopener">here&lt;/a> for use.&lt;/p>
&lt;ol>
&lt;li>For the webpack plugin part, we just need to select the hook after chunk generation according to the hooks exposed by webpack.&lt;/li>
&lt;li>Obtain the chunk source code and perform AST parsing to achieve replacement.&lt;/li>
&lt;/ol>
&lt;p>The key replacement logic is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">replaceDomainLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">source&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tokenization&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">esprima&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tokenize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">source&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tokenization&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s1">&amp;#39;String&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">mark&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">charAt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Get the string&amp;#39;s quote type
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">replaceQueue&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reg&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">mark&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> + &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> + &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">mark&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">source&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">source&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">source&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="plugin-issues-under-hmr">
&lt;a class="heading-anchor-link" href="#plugin-issues-under-hmr">Plugin issues under HMR&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugin-issues-under-hmr"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Currently, there may be replacement errors under development mode with HMR, so it is recommended to add environment judgment to the plugin, such as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;production&amp;#39;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">DomainReplacePlugin&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>At the same time, if you are building locally, pay attention to environment variable issues. It is recommended to use cross-env to solve this problem, such as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npx cross-env &lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>production npm run build:dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>After the above solution, the business code can still work normally.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/41470771/webpack-does-the-order-of-plugins-matter" target="_blank" rel="noopener">https://stackoverflow.com/questions/41470771/webpack-does-the-order-of-plugins-matter&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Errors/Missing_parenthesis_after_argument_list" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Errors/Missing_parenthesis_after_argument_list&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>process.env.NODE_ENV Explained (Simple Guide)</title><link>https://en.1991421.cn/2021/12/05/process-env-node-env/</link><pubDate>Sun, 05 Dec 2021 16:36:03 +0800</pubDate><guid>https://en.1991421.cn/2021/12/05/process-env-node-env/</guid><description>&lt;blockquote>
&lt;p>This variable is commonly encountered in frontend builds or Node.js development, but I never systematically understood its origins or impact. Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>NODE_ENV is an environment variable.&lt;/li>
&lt;li>Installing Node.js or Webpack does not set this variable; we usually need to set it ourselves. Before setting it, printing it will show undefined.&lt;/li>
&lt;li>This environment variable was introduced by Express and has now become a conventional practice.&lt;/li>
&lt;li>Whether setting NODE_ENV to development/production or other values makes a difference depends on whether the tools used rely on this value. For example, in webpack, we sometimes selectively enable certain plugin processing based on NODE_ENV, such as hash fingerprint generation.&lt;/li>
&lt;/ol>
&lt;h2 id="modifying-the-value-in-projects">
&lt;a class="heading-anchor-link" href="#modifying-the-value-in-projects">Modifying the Value in Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="modifying-the-value-in-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For CI builds, we often set this value at the Docker container level. For local development, where we need flexibility between projects, project-level control is better.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ cross-env &lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>production node testaaa.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="environment-variables">
&lt;a class="heading-anchor-link" href="#environment-variables">Environment Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>When I was doing Java development, I often had to configure Path and ClassPath, which are system environment variables.&lt;/p>
&lt;/blockquote>
&lt;h3 id="viewing-environment-variables-on-macos">
&lt;a class="heading-anchor-link" href="#viewing-environment-variables-on-macos">Viewing Environment Variables on macOS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="viewing-environment-variables-on-macos"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ &lt;span class="nb">echo&lt;/span> &lt;span class="nv">$NODE_ENV&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Done.&lt;/p></description></item><item><title>Getting grpc-web Demo Running</title><link>https://en.1991421.cn/2021/11/30/grpc-web-demo/</link><pubDate>Tue, 30 Nov 2021 20:32:24 +0800</pubDate><guid>https://en.1991421.cn/2021/11/30/grpc-web-demo/</guid><description>&lt;blockquote>
&lt;p>Recently researching the technical feasibility of blockchain wallet plugins, which involves gRPC technology. The official repo has a &lt;a href="https://github.com/grpc/grpc-web/" target="_blank" rel="noopener">demo&lt;/a>, but getting it running normally has some barriers, so I&amp;rsquo;m summarizing here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="protobuf">
&lt;a class="heading-anchor-link" href="#protobuf">protobuf&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="protobuf"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Recommended installation method, no need for additional compilation and environment variable configuration, https://formulae.brew.sh/formula/protobuf&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install protobuf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Verify successful installation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">protoc --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;!-- more -->
&lt;h3 id="protoc-gen-grpc-web">
&lt;a class="heading-anchor-link" href="#protoc-gen-grpc-web">protoc-gen-grpc-web&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="protoc-gen-grpc-web"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To generate Web client code for PB-defined services, this tool needs to be installed&lt;/p>
&lt;p>protoc-gen-grpc-web needs to be manually installed. Visit &lt;a href="https://github.com/grpc/grpc-web/releases" target="_blank" rel="noopener">https://github.com/grpc/grpc-web/releases&lt;/a>&lt;/p>
&lt;p>For example, for my Intel Mac, download protoc-gen-grpc-web-1.3.0-darwin-x86https://grpc.io/_64&lt;/p>
&lt;p>Execute the following commands&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">sudo mv ~/Downloads/protoc-gen-grpc-web-1.3.0-darwin-x86_64 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> /usr/local/bin/protoc-gen-grpc-web
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo chmod +x /usr/local/bin/protoc-gen-grpc-web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Same as above, if typing protoc-gen-grpc-web in the terminal shows recognition, it means installation was successful.&lt;/p>
&lt;p>&lt;em>protoc-gen-grpc-web&lt;/em> installation is too troublesome. Setting a flag here to maintain an npm package to make it easier for everyone to download and install.&lt;/p>
&lt;h3 id="brew-usage-supplement">
&lt;a class="heading-anchor-link" href="#brew-usage-supplement">Brew Usage Supplement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="brew-usage-supplement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;code>brew search package&lt;/code> can search for what version sources a package has&lt;/li>
&lt;li>&lt;code>brew install package@versionNumber&lt;/code> can directly install the target version&lt;/li>
&lt;/ol>
&lt;h2 id="testing">
&lt;a class="heading-anchor-link" href="#testing">Testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Generate corresponding JS code according to the defined PB format file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ protoc -I&lt;span class="o">=&lt;/span>. helloworld.proto --js_out&lt;span class="o">=&lt;/span>&lt;span class="nv">import_style&lt;/span>&lt;span class="o">=&lt;/span>commonjs:. --grpc-web_out&lt;span class="o">=&lt;/span>&lt;span class="nv">import_style&lt;/span>&lt;span class="o">=&lt;/span>commonjs,mode&lt;span class="o">=&lt;/span>grpcwebtext:.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="build-and-startup">
&lt;a class="heading-anchor-link" href="#build-and-startup">Build and Startup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-and-startup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After completing the above steps, environment-level issues are resolved. Then just follow the startup steps in the &lt;a href="https://github.com/grpc/grpc-web/blob/3fcc2a2a8a041074884ce452554662dbbe2951fb/net/grpc/gateway/examples/helloworld/README.md" target="_blank" rel="noopener">readme&lt;/a> step by step.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-10-17-172155.jpeg"
alt="https://static.1991421.cn/2022/2022-10-17-172155.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If the development machine is a Mac platform, the following two modifications are needed:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>envoy.yaml configuration modification&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">address&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">host.docker.internal&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: only this line needs to be modified&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Docker startup command adjustment - remove network setting&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker run -d -v &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>/envoy.yaml:/etc/envoy/envoy.yaml:ro &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>-p 8080:8080 -p 9901:9901 envoyproxy/envoy:v1.20.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="error">
&lt;a class="heading-anchor-link" href="#error">Error&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Http response at 400 or 500 level&lt;/p>
&lt;/blockquote>
&lt;p>After startup, if you encounter this error, it means network services are not communicating. Check if services started normally, for example, if the above proxy configuration error caused container communication issues. If an envoy proxy node goes down, it can also cause this error.&lt;/p>
&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Encountered some problems when using grpc-web. The official documentation and community are not very active, so many pitfalls have to be discovered through personal experience. Here I summarize some common issues.&lt;/p>
&lt;/blockquote>
&lt;h3 id="grpc-received-message-larger-than-max-1094796627-vs-4194304">
&lt;a class="heading-anchor-link" href="#grpc-received-message-larger-than-max-1094796627-vs-4194304">grpc: received message larger than max (1094796627 vs. 4194304)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="grpc-received-message-larger-than-max-1094796627-vs-4194304"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>When encountering this error, there are two possible situations&lt;/p>
&lt;/blockquote>
&lt;h3 id="size-settings">
&lt;a class="heading-anchor-link" href="#size-settings">Size Settings&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="size-settings"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;code>grpc.max_send_message_length&lt;/code> setting, unit is bytes, &lt;code>-1&lt;/code> means no limit&lt;/li>
&lt;li>Server/client settings are independent of each other&lt;/li>
&lt;/ol>
&lt;p>Official configuration see &lt;a href="https://grpc.github.io/grpc/core/group__grpc__arg__keys.html#gab4defdabac3610ef8a5946848592458c" target="_blank" rel="noopener">https://grpc.github.io/grpc/core/group__grpc__arg__keys.html#gab4defdabac3610ef8a5946848592458c&lt;/a>&lt;/p>
&lt;!-- more -->
&lt;h3 id="--grpc-web_out">
&lt;a class="heading-anchor-link" href="#--grpc-web_out">&amp;ndash;grpc-web_out&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="--grpc-web_out"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Determine whether the WEB output mode is grpcwebtext or grpcweb. If it&amp;rsquo;s grpcweb but ultimately sends binary, it will report received message larger than max, up to 1G.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ protoc -I&lt;span class="o">=&lt;/span>. helloworld.proto --js_out&lt;span class="o">=&lt;/span>&lt;span class="nv">import_style&lt;/span>&lt;span class="o">=&lt;/span>commonjs:. --grpc-web_out&lt;span class="o">=&lt;/span>&lt;span class="nv">import_style&lt;/span>&lt;span class="o">=&lt;/span>commonjs,mode&lt;span class="o">=&lt;/span>grpcweb:.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="413-request-entity-too-large">
&lt;a class="heading-anchor-link" href="#413-request-entity-too-large">413 (Request Entity Too Large)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="413-request-entity-too-large"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Check nginx &lt;code>client_max_body_size&lt;/code> configuration&lt;/li>
&lt;li>Confirm grpc-server side &lt;code>grpc.max_receive_message_length&lt;/code> configuration&lt;/li>
&lt;/ol>
&lt;h3 id="custom-header-fields">
&lt;a class="heading-anchor-link" href="#custom-header-fields">Custom Header Fields&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-header-fields"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Sometimes when sending requests, you want custom headers, for example to implement dynamic proxy for gRPC services.&lt;/p>
&lt;/blockquote>
&lt;p>Implementation requires 2 parts of configuration&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">GreeterClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http://&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hostname&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;:9090&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// simple unary call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">HelloRequest&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;World&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">client&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sayHello&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;X-Grpc-node&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;hello world&amp;#39;&lt;/span> &lt;span class="c1">// Custom header field
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Unexpected error for sayHello: code = &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">code&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="sb">`, message = &amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">&amp;#34;`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getMessage&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>nginx configuration to read custom fields for dynamic proxy&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">listen&lt;/span> &lt;span class="mi">9080&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">server_name&lt;/span> &lt;span class="s">localhost&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">underscores_in_headers&lt;/span> &lt;span class="no">on&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1"># Controls custom header field reading, needs to be enabled
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">location&lt;/span> &lt;span class="s">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_set_header&lt;/span> &lt;span class="s">Content-Type&lt;/span> &lt;span class="s">application/grpc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_pass&lt;/span> &lt;span class="s">grpc://&lt;/span>&lt;span class="nv">$http_x_grpc_node&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1"># Note http prefix and underscores
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="nginx-dynamic-proxy-for-grpc-services">
&lt;a class="heading-anchor-link" href="#nginx-dynamic-proxy-for-grpc-services">Nginx Dynamic Proxy for gRPC Services&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nginx-dynamic-proxy-for-grpc-services"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">location&lt;/span> &lt;span class="s">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_set_header&lt;/span> &lt;span class="s">Content-Type&lt;/span> &lt;span class="s">application/grpc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_pass&lt;/span> &lt;span class="s">grpc://&lt;/span>&lt;span class="nv">$http_x_grpc_node&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that &lt;code>http_x_grpc_node&lt;/code> is the HTTP custom header field value. If you need to proxy SSL services, it should be &lt;code>grpcs://&lt;/code>, with specific configuration as follows&lt;/p>
&lt;h3 id="proxy-grpcs">
&lt;a class="heading-anchor-link" href="#proxy-grpcs">Proxy gRPCs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="proxy-grpcs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">location&lt;/span> &lt;span class="s">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_set_header&lt;/span> &lt;span class="s">Content-Type&lt;/span> &lt;span class="s">application/grpc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_pass&lt;/span> &lt;span class="s">grpcs://&lt;/span>&lt;span class="nv">$http_x_grpc_node&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Note: file names support variables
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kn">grpc_ssl_certificate&lt;/span> &lt;span class="s">/var/www/ssl/&lt;/span>&lt;span class="nv">$http_x_grpc_ssl_cert&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">grpc_ssl_certificate_key&lt;/span> &lt;span class="s">/var/www/ssl/&lt;/span>&lt;span class="nv">$http_x_grpc_ssl_cert_key&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="note-1">
&lt;a class="heading-anchor-link" href="#note-1">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>grpc_ssl_certificate, grpc_ssl_certificate_key file values support variables&lt;/code>, so different servers can be dynamically proxied.&lt;/li>
&lt;li>&lt;code>grpc_pass/grpc_ssl_certificate&lt;/code> these directives do not support being written in if conditional blocks, so dynamic switching between grpc and grpcs proxy cannot be achieved.&lt;/li>
&lt;/ul>
&lt;h2 id="supplement">
&lt;a class="heading-anchor-link" href="#supplement">Supplement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supplement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>When determining if the RPC server started normally, you can use telnet to test port connectivity, for example &lt;code>telnet 192.168.1.203 16301&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.npmjs.com/package/google-protobuf" target="_blank" rel="noopener">https://www.npmjs.com/package/google-protobuf&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://grpc.io/docs/protoc-installation/" target="_blank" rel="noopener">https://grpc.io/docs/protoc-installation/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/grpc/grpc-web" target="_blank" rel="noopener">https://github.com/grpc/grpc-web&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/9421933/cross-origin-xmlhttprequest-in-chrome-extensions" target="_blank" rel="noopener">https://stackoverflow.com/questions/9421933/cross-origin-xmlhttprequest-in-chrome-extensions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stevenocean.github.io/2020/06/20/nginx-grpc-web-go.html" target="_blank" rel="noopener">https://stevenocean.github.io/2020/06/20/nginx-grpc-web-go.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After initial attempts, I have the following understandings:&lt;/p>
&lt;ul>
&lt;li>gRPC defaults to using Protocol Buffers to define interfaces. Therefore, using gRPC involves dealing with PB&lt;/li>
&lt;li>gRPC Web is the JavaScript implementation of the gRPC protocol in browsers, but to actually connect to gRPC servers, proxy services like Envoy/Nginx are needed&lt;/li>
&lt;li>gRPC Web enables the web to indirectly communicate with backend gRPC services&lt;/li>
&lt;/ul></description></item><item><title>NPM Package Installation - Understanding Warning Messages</title><link>https://en.1991421.cn/2021/11/29/npm-warn/</link><pubDate>Mon, 29 Nov 2021 19:36:19 +0800</pubDate><guid>https://en.1991421.cn/2021/11/29/npm-warn/</guid><description>&lt;blockquote>
&lt;p>Encountered the following message when deploying frontend static resources in Docker:
npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I&amp;rsquo;ll try to do my best with it!&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-29-192627.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>My local environment versions are as follows:&lt;/p>
&lt;ul>
&lt;li>node v14.17.0&lt;/li>
&lt;li>npm v7.x&lt;/li>
&lt;/ul>
&lt;p>After researching on Google, I found the cause. Different npm versions correspond to different lock file versions, with one specific manifestation being the different values of the &lt;code>lockfileVersion&lt;/code> field. For example, npm v6 has lockfileVersion 1, while v7 has lockfileVersion 2.&lt;/p>
&lt;p>In my case, the package-lock.json file was generated locally using npm v7, but during Docker deployment, I was using the built-in npm (v6) that comes with Node v14.17.0 for package installation, which caused this warning message.&lt;/p>
&lt;p>The solution is to change npm to v6 locally and regenerate the lock file. Alternatively, you can control the npm version to v7 in Docker.&lt;/p></description></item><item><title>Improve Frontend Code Quality with Tools — WeChat Mini Program</title><link>https://en.1991421.cn/2021/11/21/improve-code-quality-wechat-miniprogram/</link><pubDate>Sun, 21 Nov 2021 21:58:30 +0800</pubDate><guid>https://en.1991421.cn/2021/11/21/improve-code-quality-wechat-miniprogram/</guid><description>&lt;blockquote>
&lt;p>I spent the last two weeks developing WeChat Mini Programs. Their code style needs matching tooling, and the setup differs slightly from general web projects. Notes below.&lt;/p>
&lt;/blockquote>
&lt;h2 id="where-config-files-live">
&lt;a class="heading-anchor-link" href="#where-config-files-live">Where Config Files Live&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="where-config-files-live"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A typical Mini Program project looks like the following, so the root for &lt;code>package.json&lt;/code>, ESLint, and other configs is under &lt;code>miniprogram&lt;/code>, not the project root.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-21-220724.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="eslint">
&lt;a class="heading-anchor-link" href="#eslint">ESLint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="eslintignore">
&lt;a class="heading-anchor-link" href="#eslintignore">&lt;code>.eslintignore&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslintignore"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">node_modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">miniprogram_npm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">**/*.wxml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes&lt;/p>
&lt;ol>
&lt;li>Treat WXML as HTML and exclude it so ESLint rules don’t flag those files.&lt;/li>
&lt;/ol>
&lt;h3 id="eslintrcjs">
&lt;a class="heading-anchor-link" href="#eslintrcjs">&lt;code>.eslintrc.js&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslintrcjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">globals&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">App&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Page&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Component&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Behavior&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wx&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getApp&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getCurrentPages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">__wxConfig&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Declare Mini Program globals so the IDE doesn’t complain with &lt;code>eslint --no-undef&lt;/code>.&lt;/p>
&lt;h2 id="prettierrcjs">
&lt;a class="heading-anchor-link" href="#prettierrcjs">&lt;code>.prettierrc.js&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prettierrcjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">printWidth&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">120&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tabWidth&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">useTabs&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">semi&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">singleQuote&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">quoteProps&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;as-needed&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">trailingComma&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;es5&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">bracketSpacing&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">arrowParens&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;always&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">htmlWhitespaceSensitivity&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;css&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">endOfLine&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;lf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">overrides&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">files&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;*.wxml&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;html&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">files&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;*.wxss&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;css&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">files&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;*.wxs&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">parser&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;babel&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Assign different parsers to WXML/WXSS to keep formatting correct.&lt;/p>
&lt;h2 id="lint-staged">
&lt;a class="heading-anchor-link" href="#lint-staged">&lt;code>lint-staged&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lint-staged"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;lint-staged&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;**/*.{js,wxs}&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;eslint --fix&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;**/*.{wxml}&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;prettier --write&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run ESLint for JS/WXS and Prettier for WXML.&lt;/p>
&lt;p>Note: ESLint includes the Prettier plugin in this setup, so JS files still go through Prettier—no separate command needed above.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Code style is more than a consistent look; it encodes best practices that prevent common mistakes. Treat it seriously.&lt;/p></description></item><item><title>Questions About Capturing HTTPS Traffic with Whistle</title><link>https://en.1991421.cn/2021/11/13/whistlehttps/</link><pubDate>Sat, 13 Nov 2021 22:57:24 +0800</pubDate><guid>https://en.1991421.cn/2021/11/13/whistlehttps/</guid><description>&lt;blockquote>
&lt;p>Whistle is a staple in my workflow, and most sites now default to HTTPS. To proxy those requests correctly, Whistle needs a bit of configuration.&lt;/p>
&lt;p>Occasionally, things still go wrong—requests fail to appear, for example. Understanding the underlying design makes troubleshooting easier, so I&amp;rsquo;m collecting the key points here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-13-224830.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="pseudo-headers-in-chrome-devtools">
&lt;a class="heading-anchor-link" href="#pseudo-headers-in-chrome-devtools">Pseudo Headers in Chrome DevTools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pseudo-headers-in-chrome-devtools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Inspecting HTTPS requests in Chrome reveals a few special headers.&lt;/p>
&lt;h3 id="https-request-headers">
&lt;a class="heading-anchor-link" href="#https-request-headers">HTTPS Request Headers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="https-request-headers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-13-225036.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol>
&lt;li>These are &lt;code>pseudo-header fields&lt;/code>.&lt;/li>
&lt;li>HTTP/1.x uses a request line (see &lt;a href="http://httpwg.org/specs/rfc7230.html#start.line" target="_blank" rel="noopener">RFC7230 §3.1&lt;/a>) to express the target URI. HTTP/2 instead encodes method and status information in pseudo headers that begin with a colon (&lt;code>:&lt;/code>).&lt;/li>
&lt;/ol>
&lt;h3 id="http-request-headers">
&lt;a class="heading-anchor-link" href="#http-request-headers">HTTP Request Headers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="http-request-headers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-13-225128.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="the-legacy-x--prefix">
&lt;a class="heading-anchor-link" href="#the-legacy-x--prefix">The Legacy &lt;code>X-&lt;/code> Prefix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-legacy-x--prefix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>For decades the convention was to add an &lt;code>X-&lt;/code> prefix when defining custom headers—&lt;code>X&lt;/code> for extension—per &lt;a href="https://tools.ietf.org/html/rfc822" target="_blank" rel="noopener">RFC 822&lt;/a>. That&amp;rsquo;s why we still see headers like &lt;code>X-Powered-By&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>In June 2012 the IETF published &lt;a href="https://tools.ietf.org/html/rfc6648" target="_blank" rel="noopener">RFC 6648&lt;/a>, recommending that custom headers drop the &lt;code>X-&lt;/code> prefix.&lt;/p>
&lt;p>If you&amp;rsquo;re defining a permanently private header, consider namespacing it with your organization (for example, a reverse domain like &lt;code>com.example.foo&lt;/code>).&lt;/p>
&lt;h3 id="header-name-casing">
&lt;a class="heading-anchor-link" href="#header-name-casing">Header Name Casing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="header-name-casing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Header names are case-insensitive, so write them however you prefer.&lt;/p>
&lt;blockquote>
&lt;p>Each header field consists of a name followed by a colon (&amp;quot;:&amp;quot;) and the field value. Field names are case-insensitive.&lt;/p>
&lt;/blockquote>
&lt;h2 id="three-key-whistle-settings-for-https-capture">
&lt;a class="heading-anchor-link" href="#three-key-whistle-settings-for-https-capture">Three Key Whistle Settings for HTTPS Capture&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="three-key-whistle-settings-for-https-capture"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://127.0.0.1:8899/cgi-bin/rootca" target="_blank" rel="noopener">Download RootCA&lt;/a>&lt;/li>
&lt;li>Capture TUNNEL CONNECTS&lt;/li>
&lt;li>Enable HTTP/2&lt;/li>
&lt;/ul>
&lt;h3 id="installing-the-certificate">
&lt;a class="heading-anchor-link" href="#installing-the-certificate">Installing the Certificate&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-the-certificate"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After you &lt;code>download → install → trust&lt;/code> Whistle&amp;rsquo;s certificate, the telltale sign that everything works is seeing Whistle listed as the issuer when you inspect a site&amp;rsquo;s certificate.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-13-225128.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="why-mitm-works">Why MitM Works&lt;/h4>&lt;p>Without decrypting HTTPS, a proxy sees only the domain; request and response bodies remain opaque because of TLS. Whistle—and tools like Surge—solve this via a man-in-the-middle (MitM) approach.&lt;/p>
&lt;p>MitM works because the proxy presents a self-signed certificate to the client and simultaneously maintains the real TLS connection to the origin server.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-11-13-225301.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="capture-tunnel-connects">
&lt;a class="heading-anchor-link" href="#capture-tunnel-connects">Capture TUNNEL CONNECTS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="capture-tunnel-connects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If this option is disabled, Whistle can&amp;rsquo;t capture HTTPS request and response bodies.&lt;/p>
&lt;p>Why the name? Because:&lt;/p>
&lt;ol>
&lt;li>A tunnel is an HTTP application that blindly relays raw data across two connections once established.&lt;/li>
&lt;li>For HTTPS requests, the browser sends an HTTP &lt;code>CONNECT&lt;/code> method to the proxy to establish that tunnel.&lt;/li>
&lt;/ol>
&lt;h3 id="enable-http2">
&lt;a class="heading-anchor-link" href="#enable-http2">Enable HTTP/2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-http2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Disable this option and Whistle will report HTTP/1.1 for requests and may mis-handle them entirely.&lt;/p>
&lt;h2 id="when-whistle-fails-to-capture-traffic">
&lt;a class="heading-anchor-link" href="#when-whistle-fails-to-capture-traffic">When Whistle Fails to Capture Traffic&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-whistle-fails-to-capture-traffic"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="requests-missing-from-the-network-tab">
&lt;a class="heading-anchor-link" href="#requests-missing-from-the-network-tab">Requests Missing from the Network Tab&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requests-missing-from-the-network-tab"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Often the requests never hit Whistle because they bypass the proxy. A recent example from my setup:&lt;/p>
&lt;blockquote>
&lt;p>My system proxy is Surge. Chrome sends traffic to Surge, which then forwards to Whistle on port 8899. I noticed that any service on the &lt;code>192.168.x.x&lt;/code> LAN wasn&amp;rsquo;t being captured. Turns out Surge had a &amp;ldquo;skip proxy&amp;rdquo; rule for internal addresses, so those requests never reached Whistle.&lt;/p>
&lt;/blockquote>
&lt;p>When this happens, double-check that your traffic is actually routed through Whistle.&lt;/p>
&lt;h3 id="rules-not-working">
&lt;a class="heading-anchor-link" href="#rules-not-working">Rules Not Working&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rules-not-working"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Look at the request details in Whistle&amp;rsquo;s Network tab. If the rule section is empty, the rule didn&amp;rsquo;t match. At the moment, Whistle doesn&amp;rsquo;t validate rule syntax—it&amp;rsquo;s on you to ensure the configuration is correct.&lt;/p>
&lt;h2 id="extras">
&lt;a class="heading-anchor-link" href="#extras">Extras&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extras"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="nginx-and-http2">
&lt;a class="heading-anchor-link" href="#nginx-and-http2">Nginx and HTTP/2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nginx-and-http2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Nginx can&amp;rsquo;t serve different protocols on the same port across multiple virtual hosts. For example, if port 443 hosts several server blocks and you enable HTTP/2 on just one, all of them effectively speak HTTP/2.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With these pieces understood, debugging Whistle&amp;rsquo;s HTTPS capture becomes much more manageable, and you can rely on the tool confidently.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/CONNECT" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/CONNECT&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/36019868/authority-http-header-in-chrome-dev-tools" target="_blank" rel="noopener">https://stackoverflow.com/questions/36019868/authority-http-header-in-chrome-dev-tools&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/wuchangming/https-mitm-proxy-handbook/blob/master/doc/Chapter2.md" target="_blank" rel="noopener">https://github.com/wuchangming/https-mitm-proxy-handbook/blob/master/doc/Chapter2.md&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tonyxu.io/zh/posts/2018/http-deprecate-x-prefix/" target="_blank" rel="noopener">https://tonyxu.io/zh/posts/2018/http-deprecate-x-prefix/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Common Issues in WeChat Mini Program Development</title><link>https://en.1991421.cn/2021/10/29/25fe374/</link><pubDate>Fri, 29 Oct 2021 22:05:07 +0800</pubDate><guid>https://en.1991421.cn/2021/10/29/25fe374/</guid><description>&lt;blockquote>
&lt;p>Recently engaged in WeChat mini program development, encountered some pitfalls/issues during the process. Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="wechat-image-preview">
&lt;a class="heading-anchor-link" href="#wechat-image-preview">WeChat Image Preview&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wechat-image-preview"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes you need to implement long-press on images to show a menu for sharing, etc. The SDK supports this via &lt;a href="https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.previewImage.html" target="_blank" rel="noopener">wx.previewImage&lt;/a>, but currently doesn&amp;rsquo;t support custom menus.&lt;/p>
&lt;h2 id="caching">
&lt;a class="heading-anchor-link" href="#caching">Caching&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="caching"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To reduce unnecessary request overhead and improve speed, attention to caching usage is needed. For example, image resources accessed via wx.getImageInfo will still make new requests even if previously accessed, without using previous cache.&lt;/p>
&lt;h2 id="wxml-doesnt-support-js-function-calls-for-conditional-judgment">
&lt;a class="heading-anchor-link" href="#wxml-doesnt-support-js-function-calls-for-conditional-judgment">WXML Doesn&amp;rsquo;t Support JS Function Calls for Conditional Judgment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wxml-doesnt-support-js-function-calls-for-conditional-judgment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Calculations and assignments need to be done in the JS logic layer.&lt;/p>
&lt;h2 id="current-system-is-not-a-secure-proxy">
&lt;a class="heading-anchor-link" href="#current-system-is-not-a-secure-proxy">Current System Is Not a Secure Proxy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-system-is-not-a-secure-proxy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-10-29-222043.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Developer tools show proxy reminder popup, and even after selecting OK, it still reminds on next startup.&lt;/p>
&lt;p>Solution: &lt;code>Developer Tools - Settings - Proxy - Direct Connection&lt;/code>&lt;/p>
&lt;h2 id="resolution">
&lt;a class="heading-anchor-link" href="#resolution">Resolution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resolution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Different devices have different resolutions. WeChat mini programs provide a new unit rpx, where all devices are &lt;code>750rpx&lt;/code> wide. Use this for width settings during development.&lt;/p>
&lt;h2 id="developmentpreviewproduction-versions">
&lt;a class="heading-anchor-link" href="#developmentpreviewproduction-versions">Development/Preview/Production Versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="developmentpreviewproduction-versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Mini programs have development/preview/production versions&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Preview version &lt;code>has only one instance&lt;/code>, and submissions for review must be the preview version&lt;/p>
&lt;ul>
&lt;li>Submitting for review will definitely affect the preview version&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Preview in developer tools is the development version. Submissions from tools can be set as preview version on the mini program website for product/testing trials. After final approval, it can be submitted for review to publish the production version.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Multi-person collaboration requires adding others to project members with developer role permissions&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-10-29-224216.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="pitfall">
&lt;a class="heading-anchor-link" href="#pitfall">Pitfall&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pitfall"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If the approved pending release version is 2.0.0, and you resubmit a preview version also numbered 2.0.0, it automatically replaces the previous pending release version, and &lt;code>status changes to unreviewed&lt;/code>.&lt;/p>
&lt;h2 id="datasetdata">
&lt;a class="heading-anchor-link" href="#datasetdata">Data/setData&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="datasetdata"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Data displayed in WXML must be defined in data, and data updates must use setData. Direct this.data updates won&amp;rsquo;t update the view&lt;/li>
&lt;li>Variables not consumed in WXML can be stored in custom objects - data around a Page doesn&amp;rsquo;t necessarily need to be stored in data&lt;/li>
&lt;/ol>
&lt;h2 id="npm-support">
&lt;a class="heading-anchor-link" href="#npm-support">NPM Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Sometimes we need third-party packages, but projects don&amp;rsquo;t enable NPM support by default - manual operation required.&lt;/p>
&lt;/blockquote>
&lt;h3 id="enable-npm-module">
&lt;a class="heading-anchor-link" href="#enable-npm-module">Enable NPM Module&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-npm-module"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-12-25-123403.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="generate-packagejson">
&lt;a class="heading-anchor-link" href="#generate-packagejson">Generate package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="generate-packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;code>cd miniprogram&lt;/code> directory&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Run &lt;code>npm init -y&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Run &lt;code>npm install [packageName]&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Developer Tools - Tools - Build npm&lt;/p>
&lt;ul>
&lt;li>Creates miniprogram_npm under miniprogram&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Use CommonJS require to load packages in the project, like the code below&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">substr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">length&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;stringz&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>If npm build errors occur, manually edit npm-related config in &lt;code>project.config.json&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;setting&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;packNpmManually&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;packNpmRelationList&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;packageJsonPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./miniprogram/package.json&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;miniprogramNpmDistDir&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./miniprogram/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="permissions">
&lt;a class="heading-anchor-link" href="#permissions">Permissions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="permissions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Mini programs sometimes need permission settings to complete certain features.&lt;/p>
&lt;p>Permissions have two parts: 1) WeChat app permissions in the system, 2) mini program permissions within WeChat.&lt;/p>
&lt;h3 id="image-permissions">
&lt;a class="heading-anchor-link" href="#image-permissions">Image Permissions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="image-permissions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For image permissions, successful image storage requires both settings:&lt;/p>
&lt;ul>
&lt;li>iOS requires Settings - WeChat - Photos access authorization&lt;/li>
&lt;li>In mini program - top right corner icon - Settings enable Photo Album&lt;/li>
&lt;/ul>
&lt;h2 id="default-launch-page">
&lt;a class="heading-anchor-link" href="#default-launch-page">Default Launch Page&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="default-launch-page"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>The first item in the pages array in miniprogram/app.json&lt;/code> is the default launch page. If dynamic, use &lt;code>wx.redirectTo&lt;/code> for navigation in the program&lt;/p>
&lt;h2 id="tabbar-settings">
&lt;a class="heading-anchor-link" href="#tabbar-settings">TabBar Settings&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tabbar-settings"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Custom tabbar settings have 2 parts&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>app.json&lt;/strong> tabBar settings, including icon configuration&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;tabBar&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;list&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Dashboard&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pagePath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pages/dashboard/index&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/images/dashboard.png&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;selectedIconPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/images/dashboard-selected.png&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Settings&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pagePath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pages/setting/index&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/images/setting.png&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;selectedIconPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/images/setting-selected.png&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Set data-tabbar variable in specific pages&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getApiTokenKey&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">tabs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Dashboard&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">pagePath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">pages&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">dashboard&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">},&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Settings&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">pagePath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">pages&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">setting&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">currentIdx&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="error-system-error-error-code-2">
&lt;a class="heading-anchor-link" href="#error-system-error-error-code-2">Error: System Error, Error Code: 2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-system-error-error-code-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Error: System error, error code: 2, [20220103 17:25:08][wx9b4f36e257f24b17]&lt;/p>
&lt;p>Close to view documentation&lt;/p>
&lt;/blockquote>
&lt;p>Error cause: Developer tools has pre-fetch enabled, but Mini Program - Development Management - Data Pre-fetch is not enabled. If not enabled, uncheck the corresponding option in developer tools.&lt;/p>
&lt;h2 id="real-device-debugging-getapp-returns-undefined">
&lt;a class="heading-anchor-link" href="#real-device-debugging-getapp-returns-undefined">Real Device Debugging getApp Returns undefined&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="real-device-debugging-getapp-returns-undefined"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>Select Real Device Debugging 2.0&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-28-172032.jpeg"
alt="https://static.1991421.cn/2022/2022-05-28-172032.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="vconsole-button-appears-in-production-version">
&lt;a class="heading-anchor-link" href="#vconsole-button-appears-in-production-version">vConsole Button Appears in Production Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="vconsole-button-appears-in-production-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Access the development version, turn off vConsole in the mini program, then reopen the production version. This is a WeChat developer tools bug.&lt;/p>
&lt;img src="https://static.1991421.cn/2022/2022-05-29-111358.jpeg" alt="https://static.1991421.cn/2022/2022-05-29-111358.jpeg" style="zoom:67%;" />
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Continuous learning, continuous updating.&lt;/p></description></item><item><title>Hexo Blog Build Performance Optimization</title><link>https://en.1991421.cn/2021/10/23/hexo/</link><pubDate>Sat, 23 Oct 2021 20:21:55 +0800</pubDate><guid>https://en.1991421.cn/2021/10/23/hexo/</guid><description>&lt;blockquote>
&lt;p>Took advantage of the weekend to optimize my personal blog&amp;rsquo;s build performance. Previously each build took 10+ minutes, after optimization it takes 6 minutes. If we exclude search indexing, deploying just the blog to VPS takes only 3+ minutes. Here&amp;rsquo;s a summary of the optimization items.&lt;/p>
&lt;/blockquote>
&lt;h2 id="optimization-items">
&lt;a class="heading-anchor-link" href="#optimization-items">Optimization Items&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="optimization-items"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Remove certain plugins/features&lt;/p>
&lt;ul>
&lt;li>category&lt;/li>
&lt;li>baidu sitemap&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Algolia&lt;/p>
&lt;ul>
&lt;li>Remove certain indexing fields: gallery, photo, excerpt, slug&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>CI process optimization&lt;/p>
&lt;ul>
&lt;li>Cache hexo db.json/node_modules&lt;/li>
&lt;li>Automatically cancel previously running workflows using &lt;code>cancel-workflow-action&lt;/code>&lt;/li>
&lt;li>Update Algolia indexing after VPS deployment, since it doesn&amp;rsquo;t affect blog browsing, only search hit rates&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Upgrade hexo to v5, cli to v4&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>For specific configurations, you can check the source code at &lt;a href="https://github.com/alanhe421/alanhe421.github.io" target="_blank" rel="noopener">https://github.com/alanhe421/alanhe421.github.io&lt;/a>&lt;/p>
&lt;h2 id="extended-discussion">
&lt;a class="heading-anchor-link" href="#extended-discussion">Extended Discussion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extended-discussion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Regarding the issue of slow builds when Hexo has many articles, there&amp;rsquo;s an official &lt;a href="https://github.com/hexojs/hexo/issues/2579" target="_blank" rel="noopener">issue&lt;/a> discussing this. The optimization solutions are similar to what I implemented above. If the performance is still unacceptable after optimization, the only option would be to abandon using static blogs.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Slow static blog builds due to many articles/plugins is not HexoJS&amp;rsquo;s fault - this is a common problem across static site generators.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>So, if in the future the number of articles grows to the point where builds remain this slow, I might have to switch to a dynamic blog platform.&lt;/p></description></item><item><title>RTL Explained (Simple Guide)</title><link>https://en.1991421.cn/2021/10/07/rtl-intro/</link><pubDate>Thu, 07 Oct 2021 23:51:28 +0800</pubDate><guid>https://en.1991421.cn/2021/10/07/rtl-intro/</guid><description>&lt;blockquote>
&lt;p>I saw related introductions very early when playing with Ionic but didn&amp;rsquo;t understand them. Recently, I saw related articles and learned about it to fill the knowledge gap.&lt;/p>
&lt;/blockquote>
&lt;h2 id="some-rtl-sites">
&lt;a class="heading-anchor-link" href="#some-rtl-sites">Some RTL Sites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="some-rtl-sites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The websites we usually see are LTR (Left-To-Right), but there are actually some sites that are RTL (Right-To-Left), such as these:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://news.un.org/ar/" target="_blank" rel="noopener">https://news.un.org/ar/&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://wam.ae/ar" target="_blank" rel="noopener">http://wam.ae/ar&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.emaratalyoum.com/" target="_blank" rel="noopener">https://www.emaratalyoum.com/&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>They feel awkward, but they do exist. Because in the Middle East, languages like Arabic and Hebrew are read and used from right to left.&lt;/p>
&lt;p>Due to content limitations in development, like me, there&amp;rsquo;s a low probability of encountering these layout implementations, but understanding them is still beneficial for development. Moreover, if we need to develop software that supports RTL layout, we won&amp;rsquo;t be without ideas.&lt;/p>
&lt;p>RTL is primarily a UI issue that can&amp;rsquo;t avoid HTML/CSS. Fortunately, these technologies themselves support RTL.&lt;/p>
&lt;h2 id="html-rtl">
&lt;a class="heading-anchor-link" href="#html-rtl">HTML RTL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="html-rtl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The global attribute &lt;code>dir&lt;/code> is an enumerated attribute indicating the direction of text within an element.&lt;/p>
&lt;/blockquote>
&lt;h3 id="compatibility">
&lt;a class="heading-anchor-link" href="#compatibility">Compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-10-24-132921.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>IE is always so out of the ordinary, but fortunately, IE compatibility is not as crucial today, thanks to the Chromium-based Edge taking over.&lt;/p>
&lt;h2 id="css-rtl">
&lt;a class="heading-anchor-link" href="#css-rtl">CSS RTL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="css-rtl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The &lt;strong>&lt;code>direction&lt;/code>&lt;/strong> CSS property sets the direction of text and table column horizontal overflow. &lt;code>rtl&lt;/code> means right-to-left (like Hebrew or Arabic), and &lt;code>ltr&lt;/code> means left-to-right (like English and most languages).&lt;/p>
&lt;p>CSS &lt;code>direction&lt;/code> has the same effect as HTML&amp;rsquo;s &lt;code>dir&lt;/code> but with higher priority.&lt;/p>
&lt;/blockquote>
&lt;h3 id="compatibility-1">
&lt;a class="heading-anchor-link" href="#compatibility-1">Compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-10-24-133454.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="some-css-helper-tools">
&lt;a class="heading-anchor-link" href="#some-css-helper-tools">Some CSS Helper Tools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="some-css-helper-tools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using HTML/CSS can solve support issues to some extent, but sometimes systematic solutions are needed. For example, the following library can be used, which essentially involves batch processing and replacement of CSS.&lt;/p>
&lt;p>&lt;a href="https://github.com/MohammadYounes/rtlcss" target="_blank" rel="noopener">https://github.com/MohammadYounes/rtlcss&lt;/a>&lt;/p>
&lt;h2 id="react-rtl-support">
&lt;a class="heading-anchor-link" href="#react-rtl-support">React RTL Support?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-rtl-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React itself does not provide this support, but you can support it yourself or use third-party extensions, such as reading &lt;code>lang&lt;/code> and &lt;code>dir&lt;/code> values, controlling the rendering order of elements, and rendering different icons.&lt;/p>
&lt;h2 id="ant-design">
&lt;a class="heading-anchor-link" href="#ant-design">Ant Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ant-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Upon investigation, I found that Ant Design has built-in RTL support, which is quite thoughtful.&lt;/p>
&lt;p>For details, see &lt;a href="https://ant.design/components/config-provider-cn/" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Mark. Specific practice can only be continuously improved and confirmed in projects.&lt;/p>
&lt;h2 id="related-links">
&lt;a class="heading-anchor-link" href="#related-links">Related Links&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-links"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/47864242" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/47864242&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/quick-code/rtl-support-in-angular-b7de22946972" target="_blank" rel="noopener">https://medium.com/quick-code/rtl-support-in-angular-b7de22946972&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ionicframework.com/blog/ionic-and-rtl/" target="_blank" rel="noopener">https://ionicframework.com/blog/ionic-and-rtl/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/34899513/right-to-left-rtl-support-in-react" target="_blank" rel="noopener">https://stackoverflow.com/questions/34899513/right-to-left-rtl-support-in-react&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Docker PostgreSQL Deployment Issues</title><link>https://en.1991421.cn/2021/10/06/99063e04/</link><pubDate>Wed, 06 Oct 2021 23:01:48 +0800</pubDate><guid>https://en.1991421.cn/2021/10/06/99063e04/</guid><description>&lt;blockquote>
&lt;p>Recently while working on server-side development, I had a requirement to deploy db-postgres. I encountered some pitfalls during the process, so I&amp;rsquo;m documenting them here.&lt;/p>
&lt;/blockquote>
&lt;p>Deploying postgres service can be done by directly using the postgres image, but when it involves initializing the database and disk mapping, I encountered the following error in actual operation:&lt;/p>
&lt;p>&lt;code>cannot access '/docker-entrypoint-initdb.d/': Operation not permitted&lt;/code>&lt;/p>
&lt;p>The final solution was to add &lt;code>privileged: true&lt;/code> to docker-compose.&lt;/p>
&lt;p>Some online sources mentioned mounting Volume with RW permissions or adding chmod when using custom images, but both approaches didn&amp;rsquo;t work in my testing. The only solution currently is the one mentioned above.&lt;/p>
&lt;p>The problem is solved, but the related knowledge points are worth summarizing.&lt;/p>
&lt;h2 id="postgresql-deployment-solutions">
&lt;a class="heading-anchor-link" href="#postgresql-deployment-solutions">PostgreSQL Deployment Solutions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="postgresql-deployment-solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To deploy a PostgreSQL instance, you can use docker-compose to directly mount the official image and configure it in compose, or you can build your own image and then use compose for orchestration. For a single service, you can also use docker run directly.&lt;/p>
&lt;h2 id="custom-postgresql-image-configuration">
&lt;a class="heading-anchor-link" href="#custom-postgresql-image-configuration">Custom PostgreSQL Image Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-postgresql-image-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For custom PostgreSQL image configuration, the general setup is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">MAINTAINER&lt;/span>&lt;span class="s"> Alan He&amp;lt;alan@1991421.cn&amp;gt;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> postgres:latest&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> POSTGRES_USER postgres&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> POSTGRES_PASSWORD &lt;span class="s2">&amp;#34;bx*fF6xxxxxxxxgQwkG.&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> ../init.sql /docker-entrypoint-initdb.d/&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">VOLUME&lt;/span>&lt;span class="s"> /var/lib/postgresql/data&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">EXPOSE&lt;/span>&lt;span class="s"> 5432&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="explanation">
&lt;a class="heading-anchor-link" href="#explanation">Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Regarding the above configuration, you need to know the following information:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>You cannot specify the image name in dockerfile; you need to use the -t flag when executing docker build to specify it&lt;/p>
&lt;/li>
&lt;li>
&lt;p>docker-compose can directly point to a dockerfile. In this case, it can be considered as the latest image, as shown below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">build: .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>.&lt;/code> means docker-compose will look for the &lt;code>Dockerfile&lt;/code> file in the current path&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The PostgreSQL-related parameters above are environment variables, so ENV is used here. ARG/ENV are different - ARG is for building the image and explicitly defines parameters that need to be passed during use. Writing it this way means that when actually using the custom image, you don&amp;rsquo;t need to pass parameters&lt;/p>
&lt;/li>
&lt;li>
&lt;p>dockerfile only explicitly exposes container ports; which specific host machine ports they bind to is handled by compose/docker cli&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="dockerfiledocker-compose">
&lt;a class="heading-anchor-link" href="#dockerfiledocker-compose">dockerfile/docker-compose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dockerfiledocker-compose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>These two can sometimes be confusing. After going through this pitfall, I have a better understanding:&lt;/p>
&lt;ul>
&lt;li>dockerfile is for single container services. When the service itself requires special operations during startup, such as a database needing to initialize and execute certain SQL, dockerfile is very suitable, as in the problem described above.&lt;/li>
&lt;li>compose is for orchestrating multiple containers. Complex configurations within the containers themselves need to be solved within the container =&amp;gt; image.&lt;/li>
&lt;/ul>
&lt;h2 id="docker-debugging">
&lt;a class="heading-anchor-link" href="#docker-debugging">Docker Debugging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docker-debugging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Locally, for example, you can directly run and start it in WS IDE&lt;/li>
&lt;li>When running on the target server, if exceptions occur, you can use &lt;code>docker logs containerId&lt;/code> to view the logs&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Documenting for reference.&lt;/p>
&lt;h2 id="related-links">
&lt;a class="heading-anchor-link" href="#related-links">Related Links&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-links"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/66229898/docker-entrypoint-initdb-permission-denied" target="_blank" rel="noopener">https://stackoverflow.com/questions/66229898/docker-entrypoint-initdb-permission-denied&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Ionic Development Experience</title><link>https://en.1991421.cn/2021/10/02/ionic-capacitor/</link><pubDate>Sat, 02 Oct 2021 23:01:09 +0800</pubDate><guid>https://en.1991421.cn/2021/10/02/ionic-capacitor/</guid><description>&lt;blockquote>
&lt;p>Recently tried to develop an Android TV App, which involved using Ionic/Capacitor. Here I&amp;rsquo;ll organize the issues encountered during learning/development.&lt;/p>
&lt;/blockquote>
&lt;p>It&amp;rsquo;s been a while since I did App development, so I was inevitably unfamiliar with new technologies and indeed encountered some pitfalls.&lt;/p>
&lt;h2 id="theoretical-knowledge">
&lt;a class="heading-anchor-link" href="#theoretical-knowledge">Theoretical Knowledge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="theoretical-knowledge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="ionic-vs-cordova">
&lt;a class="heading-anchor-link" href="#ionic-vs-cordova">Ionic vs Cordova&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ionic-vs-cordova"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Ionic and Cordova solve problems at different levels - Ionic is more high-level&lt;/li>
&lt;li>Cordova addresses cross-platform issues, such as system-level communication, accessing system APIs, Bluetooth, storage, etc. Ionic builds on Cordova by adding basic UI components, improving development efficiency&lt;/li>
&lt;/ul>
&lt;h3 id="migration-from-cordova-to-capacitor">
&lt;a class="heading-anchor-link" href="#migration-from-cordova-to-capacitor">Migration from Cordova to Capacitor&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="migration-from-cordova-to-capacitor"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>The inactivity of the Cordova community limited Ionic, since Ionic depends on Cordova. So the official team decided to implement their own solution - Capacitor. According to the official documentation, they currently recommend using Capacitor.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Of course, those using Cordova can follow the official migration documentation&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For new projects, Capacitor is recommended&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="development-pitfalls-encountered">
&lt;a class="heading-anchor-link" href="#development-pitfalls-encountered">Development Pitfalls Encountered&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="development-pitfalls-encountered"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="android-gradle">
&lt;a class="heading-anchor-link" href="#android-gradle">Android-Gradle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="android-gradle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If Android Studio doesn&amp;rsquo;t recognize the project as an Android Module when opening it, it&amp;rsquo;s likely a Gradle build issue. Check if the version is appropriate based on console errors, confirm the version is OK, and then rebuild.&lt;/p>
&lt;h3 id="xcodeandroid-studio">
&lt;a class="heading-anchor-link" href="#xcodeandroid-studio">Xcode/Android Studio&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="xcodeandroid-studio"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since we need to package APK/iOS apps, basic operations/debugging with the corresponding platform tools are required&lt;/p>
&lt;ul>
&lt;li>The build tools correspond to Xcode/Android. If the target system version doesn&amp;rsquo;t exist locally, you need to install/download the corresponding SDK to ensure simulator debugging works&lt;/li>
&lt;/ul>
&lt;h3 id="android-tv">
&lt;a class="heading-anchor-link" href="#android-tv">Android TV&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="android-tv"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Android TV doesn&amp;rsquo;t have a built-in browser by default&lt;/p>
&lt;h2 id="android-studio-proxy">
&lt;a class="heading-anchor-link" href="#android-studio-proxy">Android Studio Proxy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="android-studio-proxy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Download addresses for SDKs and other resources are all overseas, so pay attention to proxy issues, otherwise downloads may fail&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>After some effort, I finally completed the App development. It worked fine in the simulator, but failed on the actual device because my home TV&amp;rsquo;s Android version was too old (5.1)&lt;/li>
&lt;li>Although it failed, going through this process served as a good opportunity to update my App development skills&lt;/li>
&lt;li>It&amp;rsquo;s clear that Ionic is developing quite well - the community remains active, and the development experience is quite nice. The underlying dependency has moved from Cordova to Capacitor, which means they&amp;rsquo;re no longer constrained by external factors. This is good news for regular developers, as there&amp;rsquo;s active maintenance and updates&lt;/li>
&lt;/ul></description></item><item><title>Nginx Access Error Caused by File Permissions</title><link>https://en.1991421.cn/2021/09/29/87015e79/</link><pubDate>Wed, 29 Sep 2021 19:42:21 +0800</pubDate><guid>https://en.1991421.cn/2021/09/29/87015e79/</guid><description>&lt;blockquote>
&lt;p>Recently, personal image upload tool uploaded images, when HTTP access failed, all reported 403. Initially thought it was nginx anti-hotlinking settings, but finally determined the direct cause was insufficient file permissions.&lt;/p>
&lt;/blockquote>
&lt;p>Recording this problem here&lt;/p>
&lt;h2 id="about-403">
&lt;a class="heading-anchor-link" href="#about-403">About 403&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="about-403"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Status code &lt;strong>&lt;code>403 Forbidden&lt;/code>&lt;/strong> represents client error, meaning the server has ability to handle the request, but refuses authorization access.&lt;/p>
&lt;p>This status is similar to &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/401" target="_blank" rel="noopener">&lt;code>401&lt;/code>&lt;/a>, but after entering this status, verification cannot continue. This access is permanently prohibited and closely related to application logic (e.g., incorrect password).&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>401 and 403 are similar, but still different. 401 is login not successful, while 403 has login identity but partial permissions insufficient.&lt;/li>
&lt;/ul>
&lt;h2 id="fs-acl">
&lt;a class="heading-anchor-link" href="#fs-acl">FS-ACL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fs-acl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-09-29-194925.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After analysis, located that this 403 is caused by nginx container insufficient permissions to access file resources. As shown, files SCP&amp;rsquo;d to server show permissions as &lt;code>600&lt;/code>, meaning &lt;code>only owner root has read/write permissions.&lt;/code> But nginx requests access to image static resources as nginx user, therefore doesn&amp;rsquo;t have read permission.&lt;/p>
&lt;p>Solution is to grant file group/other users read permission. For example &lt;code>644&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-09-29-220556.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="scp-rsync">
&lt;a class="heading-anchor-link" href="#scp-rsync">SCP, Rsync&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scp-rsync"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>My image upload here uses self-written upload tool, so need targeted solution.&lt;/p>
&lt;p>For upload tool, I used SCP command, found SCP doesn&amp;rsquo;t support modifying file permissions, so had to switch to Rsync, specific code as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">rsync -p --chmod&lt;span class="o">=&lt;/span>&lt;span class="nv">Du&lt;/span>&lt;span class="o">=&lt;/span>rwx,Dgo&lt;span class="o">=&lt;/span>rx,Fu&lt;span class="o">=&lt;/span>rw,Fog&lt;span class="o">=&lt;/span>r ./temp/&lt;span class="o">{&lt;/span>query&lt;span class="o">}&lt;/span> &lt;span class="nv">$user&lt;/span>@&lt;span class="nv">$server&lt;/span>:&lt;span class="nv">$destination&lt;/span>/&lt;span class="nv">$year&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>scp parameter -p preserves file permission modes, not referring to file ACL, so doesn&amp;rsquo;t work&lt;/li>
&lt;li>rsync&amp;rsquo;s -p can preserve file permissions, so can use, but here because I also need to ultimately modify file permissions, added chmod&lt;/li>
&lt;li>Above chmod configuration effect is &lt;code>folders 755, files 644&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>After above modifications, re-uploaded resources, fixed&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Whenever encountering &lt;code>SCP&lt;/code> file upload while file permissions need adjustment, can consider switching to &lt;code>Rsync&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Always thought nginx reporting 403 would be container layer triggering anti-hotlinking/or backend business code reporting insufficient permissions, now one more possibility: container itself insufficient permissions when accessing server storage resources.&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Online Issue - ERR_CONNECTION_CLOSED</title><link>https://en.1991421.cn/2021/09/21/err-connection-closed/</link><pubDate>Tue, 21 Sep 2021 09:12:16 +0800</pubDate><guid>https://en.1991421.cn/2021/09/21/err-connection-closed/</guid><description>&lt;blockquote>
&lt;p>During the regular Thursday technical meeting, someone raised an online issue. There&amp;rsquo;s a table header filter on the web that reports error &lt;code>ERR_CONNECTION_CLOSED&lt;/code> when selecting all values for a request, but returns 200 normally when selecting fewer values. Because the meaning of &lt;code>ERR_CONNECTION_CLOSED&lt;/code> wasn&amp;rsquo;t clear, the troubleshooting process took some detours. Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="err_connection_closed">
&lt;a class="heading-anchor-link" href="#err_connection_closed">ERR_CONNECTION_CLOSED&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="err_connection_closed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Chrome reports request errors but without status codes, so initially it wasn&amp;rsquo;t clear whether it was a server-side or browser-level error.&lt;/p>
&lt;p>But generally, we start troubleshooting from the frontend.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-09-21-101320.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="request-body-limit">
&lt;a class="heading-anchor-link" href="#request-body-limit">Request Body Limit?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="request-body-limit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Phenomenologically, reducing parameter values returns 200, while more parameter values cause errors. So initially it was judged that it might be a request body size limit.&lt;/p>
&lt;p>Therefore, I randomly constructed a longer array parameter and found that the backend returned information normally (though it reported illegal parameter errors), thus disproving the body size limit theory.&lt;/p>
&lt;h2 id="header-length-limit">
&lt;a class="heading-anchor-link" href="#header-length-limit">Header Length Limit?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="header-length-limit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Noticed another feature when errors occur - the current URL is passed in the request header, and when table header filtering operates, the URL also updates. So it might be a header length issue.&lt;/p>
&lt;p>When errors occur, the request header is about 10KB. To confirm this, I customized a header field, filled it with values to reach 10KB length, and tested with a value that previously returned 200 normally. Found it still reported errors.&lt;/p>
&lt;p>Therefore, it can be confirmed that it&amp;rsquo;s not a business-level error caused by some parameter value, but rather a request header length limitation.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Once the cause is found, the solution becomes clear&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Increase container header length limit&lt;/p>
&lt;ul>
&lt;li>HTTP itself doesn&amp;rsquo;t limit req.header length, only individual containers have limits&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Modify the request header to avoid passing overly large request headers. For example, here a long URL was being passed as a header parameter just for logging purposes. There&amp;rsquo;s no need to pass the complete URL parameter, so we ultimately decided to use a truncation solution - limit the maximum URL length. Also note that overly long URLs themselves are unreasonable because browsers also have URL length limitations.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Need to know that this error is caused by header length limitations, but if body length exceeds limits, this error can also occur.&lt;/p>
&lt;p>Looking back at the error message ERR_CONNECTION_CLOSED, it makes sense now&lt;/p>
&lt;blockquote>
&lt;p>HTTP connections are built on top of TCP connections. As above, when header length exceeds limits, the server directly closes that TCP connection. Whereas the so-called 500, 400 errors are just HTTP connection endings, but the TCP connection still exists. There&amp;rsquo;s a fundamental difference between the two.&lt;/p>
&lt;p>Since it&amp;rsquo;s a connection, the request must have been normally sent to the server, but at the server container layer, it errored due to length limit exceedance.&lt;/p>
&lt;/blockquote>
&lt;h2 id="node--max-http-header-size">
&lt;a class="heading-anchor-link" href="#node--max-http-header-size">node/&amp;ndash;max-http-header-size&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="node--max-http-header-size"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Problem solved. To reinforce understanding, here&amp;rsquo;s using node as an example to reproduce the issue&lt;/p>
&lt;p>Node&amp;rsquo;s default header size limit is 8KB. So I added a larger custom header, requested the Node server, and the request errored. After restarting Node with a header limit specified as 1500000, it returned 200.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Start server
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">node&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="nx">max&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">http&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">header&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">size&lt;/span> &lt;span class="mi">1500000&lt;/span> &lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">js&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// JS custom header field information
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setRequestHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;...&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Generally, we&amp;rsquo;re used to judging backend errors based on Response Status, but for example, the above error doesn&amp;rsquo;t have a status, yet it&amp;rsquo;s indeed a server-side error.&lt;/p>
&lt;p>Therefore, we need to have this awareness: server-side errors don&amp;rsquo;t necessarily have responseStatus.&lt;/p></description></item><item><title>Contributing TS Type Declarations to Open Source Projects</title><link>https://en.1991421.cn/2021/09/12/contribute-ts-types/</link><pubDate>Sun, 12 Sep 2021 11:40:11 +0800</pubDate><guid>https://en.1991421.cn/2021/09/12/contribute-ts-types/</guid><description>&lt;blockquote>
&lt;p>I recently authored Redux typings for an open-source project and noted the key takeaways so I can repeat the process faster next time.&lt;/p>
&lt;/blockquote>
&lt;h2 id="why-declaration-files-matter">
&lt;a class="heading-anchor-link" href="#why-declaration-files-matter">Why declaration files matter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-declaration-files-matter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>They power editor niceties such as auto-complete and inline API hints.&lt;/li>
&lt;li>They give downstream consumers type safety.&lt;/li>
&lt;/ol>
&lt;h2 id="ways-to-create-and-ship-declaration-files">
&lt;a class="heading-anchor-link" href="#ways-to-create-and-ship-declaration-files">Ways to create and ship declaration files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ways-to-create-and-ship-declaration-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Write your library in TypeScript and compile with &lt;code>tsc --declaration&lt;/code>. The compiler emits both the &lt;code>.js&lt;/code> output and the matching &lt;code>.d.ts&lt;/code> files.&lt;/li>
&lt;li>Hand-write &lt;code>.d.ts&lt;/code> files. This is common for inactive packages that cannot ship types themselves. Publish them to DefinitelyTyped so consumers can install &lt;code>@types/&amp;lt;package&amp;gt;&lt;/code>.&lt;/li>
&lt;li>Keep local &lt;code>.d.ts&lt;/code> files inside a project and point to them via &lt;code>types&lt;/code> or &lt;code>typeRoots&lt;/code> in &lt;code>tsconfig.json&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h2 id="common-questions">
&lt;a class="heading-anchor-link" href="#common-questions">Common questions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-questions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>How do I reference another library&amp;rsquo;s types?&lt;/strong> Import them directly inside the declaration file.&lt;/li>
&lt;li>&lt;strong>How do I describe generator functions?&lt;/strong> Use the &lt;code>Generator&lt;/code> return type instead of &lt;code>function*&lt;/code> syntax in the declaration.&lt;/li>
&lt;li>&lt;strong>How do I declare exported variables?&lt;/strong> Declare them explicitly, for example &lt;code>export const foo: Foo;&lt;/code> inside the &lt;code>.d.ts&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h2 id="sample-code">
&lt;a class="heading-anchor-link" href="#sample-code">Sample code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sample-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The typings I contributed live here: &lt;a href="https://github.com/adobe/redux-saga-promise" target="_blank" rel="noopener">https://github.com/adobe/redux-saga-promise&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Declaration files feel tedious the first time around, but once you understand the patterns, adding typings to open-source projects becomes straightforward.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://ts.xcatliu.com/basics/declaration-files.html#declare-function" target="_blank" rel="noopener">https://ts.xcatliu.com/basics/declaration-files.html#declare-function&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/53550649/how-to-reference-redux-types-in-index-d-ts-typescript-definition-file" target="_blank" rel="noopener">https://stackoverflow.com/questions/53550649/how-to-reference-redux-types-in-index-d-ts-typescript-definition-file&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/adobe/redux-saga-promise" target="_blank" rel="noopener">https://github.com/adobe/redux-saga-promise&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Dockerizing Frontend Build and Deployment</title><link>https://en.1991421.cn/2021/09/05/docker/</link><pubDate>Sun, 05 Sep 2021 23:08:11 +0800</pubDate><guid>https://en.1991421.cn/2021/09/05/docker/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;ve been working on an open-source project recently, so I can step away from the company&amp;rsquo;s mature CI/CD and experiment on my own. To lower the deployment barrier for users, I decided to use Docker to build deployment images, addressing deployment efficiency issues.&lt;/p>
&lt;/blockquote>
&lt;h2 id="dockerfile">
&lt;a class="heading-anchor-link" href="#dockerfile">dockerfile&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dockerfile"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here is the Dockerfile for building the image, for example named &lt;code>build.Dockerfile&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> node:14.17.0 as builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> NODE_ENV production&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34; ------------------Web Build --------------------&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /management-web&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> . /management-web&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> npm install --registry&lt;span class="o">=&lt;/span>https://registry.npm.taobao.org&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> npm run build&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34; ------------------Web Container Deployment &amp;amp; Startup --------------------&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> nginx:1.19.2&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --from&lt;span class="o">=&lt;/span>builder /management-web/build /usr/share/nginx/html&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> deploy/nginx/conf.d /etc/nginx/conf.d&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">EXPOSE&lt;/span>&lt;span class="s"> 80&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="explanation">
&lt;a class="heading-anchor-link" href="#explanation">Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>This image implements multi-stage build: static resource packaging → Nginx hosting static resources.&lt;/li>
&lt;li>To build the image, execute: &lt;code>docker build -t management-web:1.0 --file build.Dockerfile .&lt;/code>&lt;/li>
&lt;li>The nginx config copy in the above configuration is to copy custom settings into the nginx container.&lt;/li>
&lt;li>For full-stack deployments, use &lt;code>docker-compose&lt;/code> for container orchestration.&lt;/li>
&lt;li>Since it&amp;rsquo;s a multi-stage build, the node part is just a intermediate stage. The final image size is nginx image + copied static resources (here it&amp;rsquo;s &lt;code>143MB&lt;/code>).&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Previously, deployment required a lot of tedious command configurations. Now, you just need to write the configuration once, build the image, install Docker, and start the initial container instance.&lt;/p></description></item><item><title>Lesser-Known TypeScript Types</title><link>https://en.1991421.cn/2021/09/05/typescript/</link><pubDate>Sun, 05 Sep 2021 22:14:09 +0800</pubDate><guid>https://en.1991421.cn/2021/09/05/typescript/</guid><description>&lt;blockquote>
&lt;p>TypeScript is now the default in many teams. Types act as documentation and safety nets. Still, some types aren’t used often—I didn’t truly understand them until I dug deeper. Here are a few worth noting.&lt;/p>
&lt;/blockquote>
&lt;h2 id="never">
&lt;a class="heading-anchor-link" href="#never">&lt;code>never&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="never"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Key points:&lt;/p>
&lt;ol>
&lt;li>&lt;code>never&lt;/code> is the bottom type.&lt;/li>
&lt;li>It’s useful in exhaustive checks to ensure new cases aren’t forgotten.&lt;/li>
&lt;li>&lt;code>void&lt;/code> means a function returns &lt;code>undefined&lt;/code>; &lt;code>never&lt;/code> means it never returns.&lt;/li>
&lt;/ol>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">Circle&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">kind&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;circle&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">radius&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">Square&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">kind&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;square&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sideLength&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">type&lt;/span> &lt;span class="nx">Shape&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Circle&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">Square&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">getArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">shape&lt;/span>: &lt;span class="kt">Shape&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">shape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">kind&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;circle&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PI&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">shape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">radius&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;square&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">shape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sideLength&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">default&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">exhaustiveCheck&lt;/span>: &lt;span class="kt">never&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">shape&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">exhaustiveCheck&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you later add &lt;code>Triangle&lt;/code> to &lt;code>Shape&lt;/code>, the assignment to &lt;code>never&lt;/code> fails, reminding you to update the switch.&lt;/p>
&lt;p>Further reading:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/40251524/typescript-never-type-inference" target="_blank" rel="noopener">https://stackoverflow.com/questions/40251524/typescript-never-type-inference&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Bottom_type" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Bottom_type&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fullstackbb.com/typescript/never-type-in-typescript/" target="_blank" rel="noopener">https://fullstackbb.com/typescript/never-type-in-typescript/&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="arraylike-promiselike">
&lt;a class="heading-anchor-link" href="#arraylike-promiselike">&lt;code>ArrayLike&lt;/code>, &lt;code>PromiseLike&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="arraylike-promiselike"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript’s lib definitions include “like” types. &lt;code>ArrayLike&amp;lt;T&amp;gt;&lt;/code> differs from &lt;code>Array&amp;lt;T&amp;gt;&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">ArrayLike&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">length&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">readonly&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">n&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">T&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nb">Array&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">length&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">toString&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">pop&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">T&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>ArrayLike&lt;/code> only requires an indexed collection with a length. &lt;code>Array&lt;/code> has full methods like &lt;code>pop&lt;/code>.&lt;/p>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">getSize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>: &lt;span class="kt">ArrayLike&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">any&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">number&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getSize&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]));&lt;/span> &lt;span class="c1">// ok
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">fn() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getSize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// ok with ArrayLike; would fail with Array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Useful for &lt;code>arguments&lt;/code> or DOM collections that aren’t real arrays.&lt;/p>
&lt;p>More info: &lt;a href="https://stackoverflow.com/questions/43712705/why-does-typescript-use-like-types" target="_blank" rel="noopener">https://stackoverflow.com/questions/43712705/why-does-typescript-use-like-types&lt;/a>&lt;/p>
&lt;h2 id="closing-thoughts">
&lt;a class="heading-anchor-link" href="#closing-thoughts">Closing Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closing-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you only ever use &lt;code>interface&lt;/code>, &lt;code>Pick&lt;/code>, &lt;code>Omit&lt;/code>, and unions, you’re missing a lot of TypeScript’s power. Learn these niche types to write more expressive, safer code.&lt;/p></description></item><item><title>Online Issue - Rendering Anomalies</title><link>https://en.1991421.cn/2021/08/15/da73dad/</link><pubDate>Sun, 15 Aug 2021 20:33:45 +0800</pubDate><guid>https://en.1991421.cn/2021/08/15/da73dad/</guid><description>&lt;blockquote>
&lt;p>Recently, the production environment monitoring system reported a frontend rendering error. I proceeded to analyze it, and here&amp;rsquo;s a summary record.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error-information">
&lt;a class="heading-anchor-link" href="#error-information">Error Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>this.service.apply(&amp;hellip;).then(&amp;hellip;).catch(&amp;hellip;).finally is not a function&lt;/p>
&lt;h2 id="error-environment">
&lt;a class="heading-anchor-link" href="#error-environment">Error Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Mac OS 10.14.6 Mojave&lt;/li>
&lt;li>Chrome 92.0.4515.131 Latest version&lt;/li>
&lt;li>WebKit 537.36&lt;/li>
&lt;li>ahooks v2.9.6&lt;/li>
&lt;/ul>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on the error information, it appears to be related to compatibility issues, since &lt;code>Promise.finally&lt;/code> is a function that returns a promise.&lt;/p>
&lt;p>According to MDN compatibility information, Chrome should be fine, and the user reporting the error has the latest version of Chrome.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-15-203716.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="user-trajectory---reproduction">
&lt;a class="heading-anchor-link" href="#user-trajectory---reproduction">User Trajectory - Reproduction?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="user-trajectory---reproduction"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Following the user operations recorded in the tracking data, re-executing the operations did not reproduce the error.&lt;/p>
&lt;h3 id="ahooks">
&lt;a class="heading-anchor-link" href="#ahooks">ahooks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ahooks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Based on the error, searching in the ahooks repository did find other users reporting the same error. The direct cause is that the ahooks module doesn&amp;rsquo;t provide compatibility support for promise.finally, so in unsupported browsers this error will occur, such as in WeChat on iOS, or if used in mini-programs.&lt;/p>
&lt;p>The official fix was on 2021/7/22, so only versions after that would be fixed. The version currently used in my project is &lt;code>v2.9.6&lt;/code>, which was released on 2021/2/1, so if this error occurs in unsupported browsers, it makes sense. But the current problem is that the user&amp;rsquo;s browser version is quite high, so theoretically this error shouldn&amp;rsquo;t occur.&lt;/p>
&lt;p>Based on this, the exact cause of this error cannot be completely determined at this time. Currently, the only approach is to upgrade this module, perform regression testing, deploy, and then observe whether similar online errors still occur.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This problem is not entirely clear at the moment, but this is the only way to address it. After all, looking directly at the error, it does appear to be a compatibility issue. At the same time, this error has made me realize the importance of user operation tracking/client version environment and other tracking information.&lt;/p>
&lt;h2 id="related-materials">
&lt;a class="heading-anchor-link" href="#related-materials">Related Materials&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-materials"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://github.com/alibaba/hooks/pull/1058/files" target="_blank" rel="noopener">https://github.com/alibaba/hooks/pull/1058/files&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://github.com/zloirock/core-js/issues/905" target="_blank" rel="noopener">https://github.com/zloirock/core-js/issues/905&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Zip Download Fails to Unzip in Frontend — Fix</title><link>https://en.1991421.cn/2021/08/08/frontend-zip-download-fail/</link><pubDate>Sun, 08 Aug 2021 12:23:24 +0800</pubDate><guid>https://en.1991421.cn/2021/08/08/frontend-zip-download-fail/</guid><description>&lt;blockquote>
&lt;p>Downloading a zip via frontend succeeded, but unzip errored “inappropriate file type or format”. Postman could download and unzip fine. Without backend changes, frontend had to fix it. Weekend investigation below.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error">
&lt;a class="heading-anchor-link" href="#error">Error&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-08-123629.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>报错如上&lt;/p>
&lt;h3 id="code">
&lt;a class="heading-anchor-link" href="#code">Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Relevant backend and frontend snippets:&lt;/p>
&lt;p>后端&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/download-binary&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">currentPath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cwd&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">currentPath&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/static/file/test.zip`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;binary&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Length&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;application/zip&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;binary&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前端&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">downloadFileClick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">addResponseType&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">xhr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">XMLHttpRequest&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;get&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;/test/download-binary&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setRequestHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Content-Type&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;application/json;charset=UTF-8&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onload&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saveFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">saveFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">content&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Blob&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nx">content&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/zip&amp;#39;&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">URL&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createObjectURL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;test.zip&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">click&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>At first I suspected Blob construction issues — tried different types/encodings — no luck. MDN reminded me that XHR supports &lt;code>responseType&lt;/code>, so I added:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">responseType&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;blob&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Unzipping then worked reliably. Conclusion: when the backend returns binary data, set &lt;code>xhr.responseType = 'blob'&lt;/code>. A new‑tab navigation download doesn’t hit this because the browser handles it.&lt;/p>
&lt;p>问题虽然解决，但为什么这样就行？这里整体分析下。&lt;/p>
&lt;h2 id="分析">
&lt;a class="heading-anchor-link" href="#%e5%88%86%e6%9e%90">分析&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="分析"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="xhrresponsetype">
&lt;a class="heading-anchor-link" href="#xhrresponsetype">xhr.responseType&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="xhrresponsetype"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>MDN文档上关于responseType这样描述&lt;/p>
&lt;blockquote>
&lt;p>XMLHttpRequest.responseType 属性是一个枚举类型的属性，返回响应数据的类型。它允许我们手动的设置返回数据的类型。如果我们将它设置为一个空字符串，它将使用默认的&amp;quot;text&amp;quot;类型。&lt;/p>
&lt;/blockquote>
&lt;p>断点查看下xhr.responseType设定对于返回response体的影响&lt;/p>
&lt;ul>
&lt;li>
&lt;p>默认文本&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-08-124913.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>blob&lt;/p>
&lt;p>​ &lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-08-124939.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>可以看出response类型有区别&lt;/p>
&lt;p>因此可以说后端返回的response[content-type]在xhr中并没有用，仍然需要手动指定。但response[content-type]一点用都没有？错。&lt;/p>
&lt;h3 id="response-headerscontent-type">
&lt;a class="heading-anchor-link" href="#response-headerscontent-type">Response Headers[Content-Type]&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="response-headerscontent-type"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>尝试修改response[content-type]来对比测试&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-08-125816.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-08-130141.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>chrome下预览返回结果，&lt;/p>
&lt;ol>
&lt;li>对于二进制还是文本，预览是chrome都会文本形式进行预览，所以直观来看内容一样&lt;/li>
&lt;li>但是在JS中仍然会有影响，如果后端返回文本形式的内容，最终下载解压仍然失败。&lt;/li>
&lt;li>Content-Length在文本形式返回及二进制返回大小并不相同，比如这里如果是Text，长度是&lt;code>350&lt;/code>，而Blob是&lt;code>318&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>所以结论就是前端xhr要明确responseType同时后端需要response[content-type]设置二进制返回，缺一不可。&lt;/strong>&lt;/p>
&lt;h2 id="postman">
&lt;a class="heading-anchor-link" href="#postman">Postman&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="postman"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>使用postman测试，确保后端代码返回二进制数据，前端代码不指定responseType下解压报错。而postman却解压正常。&lt;/p>
&lt;p>原因就容易解释了，浏览器异步请求需要指定XHR来确保返回数据正确进行格式解析，而postman请求的内部实现无论是利用的浏览器异步还是别的程序编程，总之正确的解析了返回的二进制数据，因此没问题。&lt;/p>
&lt;h2 id="dataurl">
&lt;a class="heading-anchor-link" href="#dataurl">DataURL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dataurl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>当然除了上述办法外还有一个办法即DataURL，这也是在一开始我还没解决找到解压根本原因前的解决方案。&lt;/p>
&lt;p>后端&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/download-base64&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">currentPath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cwd&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">currentPath&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/static/file/test.zip`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;base64&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="nx">content&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前端&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">downloadFileBase64Click&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">xhr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">XMLHttpRequest&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;get&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;/test/download-base64&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setRequestHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Content-Type&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;application/json&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onload&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saveBase64File&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">content&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">saveBase64File&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">content&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;data:application/zip;base64,&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;test.zip&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">click&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如上即可解决。&lt;/p>
&lt;p>需要注意的是，采用base64，后端返回的内容长度是&lt;code>438&lt;/code>，相较二进制的&lt;code>318&lt;/code>，长度会变大。&lt;/p>
&lt;p>查资料了解：&lt;code>Base64编码的数据体积通常是原数据的体积4/3&lt;/code>&lt;/p>
&lt;h2 id="binary-vs-dataurl">
&lt;a class="heading-anchor-link" href="#binary-vs-dataurl">Binary vs DataURL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="binary-vs-dataurl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>搞清楚了问题所在，且了解到有两种方案，那哪种方案更好呢。首先了解下利弊&lt;/p>
&lt;ol>
&lt;li>Base64编码后数据体积变大&lt;/li>
&lt;li>对于文件，采用Base64编码一般是用于降低HTTP会话请求数量&lt;/li>
&lt;/ol>
&lt;p>个人觉得如果是异步下载，优先二进制流即可，没必要造成后端编码成本及请求体积增大。&lt;/p>
&lt;p>DataURL适用于比如网页加载部分图片等资源，不希望占用HTTP会话，且有些简单的图片资源需要动态生成，那么可以使用DataURL&lt;/p>
&lt;h2 id="延伸">
&lt;a class="heading-anchor-link" href="#%e5%bb%b6%e4%bc%b8">延伸&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="延伸"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="utf8-vs-utf-8">
&lt;a class="heading-anchor-link" href="#utf8-vs-utf-8">utf8 vs utf-8&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="utf8-vs-utf-8"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>利用Node来编写下载支持的后端代码时注意到fs.readFileSync的编码有两个类似的值utf8，utf-8。这样傻傻分不清。查了下资料，这里两个值使用哪个均可，准确来说&lt;code>utf8是utf-8的别名&lt;/code>。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-08-150354.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>WHY？因为有些框架下，对于编码常量上，不支持中线，因此才有了utf8这个值，node是跨平台运行环境，对此做了兼容处理。&lt;/p>
&lt;h3 id="wireshark抓包">
&lt;a class="heading-anchor-link" href="#wireshark%e6%8a%93%e5%8c%85">Wireshark抓包&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wireshark抓包"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Chrome下看到的请求无法以16进制/二进制形式查看数据，肉眼看到的只是文本形式，因此觉得类似，但毕竟内容不同，那么具体怎么不同，这里可以使用抓包工具&lt;code>Wireshark&lt;/code>来真正查看HTTP响应报文数据。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-13-213946.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-13-220144.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol>
&lt;li>截图仔细对比16进制的数据，可以看出HTTP请求响应包-文本/二进制数据的内容是不同的。当然如果看右侧的文本形式还是会觉得相同。这点也与chrome中看到的一致&lt;/li>
&lt;li>留意红框选中的length值，并不是前面讲到的内容长度350，这是因为Wireshark在包列表页展示的length为以太网帧的长度，这其中当然包含了在chrome看到的请求响应中的content-length即应用层HTTP-请求响应体的内容长度&lt;/li>
&lt;li>虽然有返回不同的文本/二进制，但实际的数据传输最终都会是二进制01，这里只是显示为16进制格式而已，在Wireshark下也可以切换为二进制展示&lt;/li>
&lt;/ol>
&lt;h2 id="写在最后">
&lt;a class="heading-anchor-link" href="#%e5%86%99%e5%9c%a8%e6%9c%80%e5%90%8e">写在最后&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="写在最后"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>关于文中提到的代码demo，可以在&lt;a href="https://github.com/alanhe421/express-demo/blob/master/views/index.ejs" target="_blank" rel="noopener">这里&lt;/a>找到&lt;/p>
&lt;p>问题到此为止，结论也清晰了，Mark。&lt;/p>
&lt;h2 id="相关文档">
&lt;a class="heading-anchor-link" href="#%e7%9b%b8%e5%85%b3%e6%96%87%e6%a1%a3">相关文档&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="相关文档"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/809620/utf8-or-utf-8" target="_blank" rel="noopener">https://stackoverflow.com/questions/809620/utf8-or-utf-8&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>About .DS_Store on Mac</title><link>https://en.1991421.cn/2021/08/07/mac-ds-store/</link><pubDate>Sat, 07 Aug 2021 14:49:44 +0800</pubDate><guid>https://en.1991421.cn/2021/08/07/mac-ds-store/</guid><description>&lt;blockquote>
&lt;p>Our team recently received a notice: the security team found .DS_Store files uploaded to the company CDN. We were asked to avoid committing this file.&lt;/p>
&lt;p>The question is: why does this file affect security, and how do we avoid it? Here is a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="ds_store">
&lt;a class="heading-anchor-link" href="#ds_store">.DS_Store&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ds_store"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>First, understand the file. .DS_Store is a hidden file generated by macOS, storing custom attributes for files or directories.&lt;/p>
&lt;/blockquote>
&lt;p>For example, create an empty folder and a file, then browse via command line and you will see .DS_Store.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-07-150422.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>You can see it records disk paths and other info. If abused, it can be a security risk.&lt;/p>
&lt;p>&lt;em>Note&lt;/em>&lt;/p>
&lt;ol>
&lt;li>Even if you enable showing hidden files on Mac, Finder still will not display .DS_Store. You need to use the command line or an IDE with &amp;ldquo;show hidden files&amp;rdquo; enabled.&lt;/li>
&lt;/ol>
&lt;p>If the file is removed, does it affect macOS? Sources say no, so it is safe to delete.&lt;/p>
&lt;h2 id="mitigation">
&lt;a class="heading-anchor-link" href="#mitigation">Mitigation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mitigation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since .DS_Store poses a security risk, here are the measures:&lt;/p>
&lt;ol>
&lt;li>Use &lt;code>.gitignore&lt;/code> to ensure it is not committed.&lt;/li>
&lt;li>When sharing zip archives, .DS_Store may be included. I recommend &lt;a href="https://www.keka.io/en/" target="_blank" rel="noopener">Keka&lt;/a> and enabling the option to exclude &lt;code>.DS_Store&lt;/code>. Note: macOS may regenerate .DS_Store as you modify folders.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-08-07-150830.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="3">
&lt;li>
&lt;p>Run this command so SMB/network shares do not create .DS_Store.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool TRUE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Run this command to delete all .DS_Store files under the current folder.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> find . -name &lt;span class="s2">&amp;#34;.DS_Store&amp;#34;&lt;/span> -print -delete
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In addition, if you use tools to upload assets to a CDN, you can have those tools filter &lt;code>.DS_Store&lt;/code> automatically.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It is a small issue but worth paying attention to. Marked.&lt;/p></description></item><item><title>react/display-name</title><link>https://en.1991421.cn/2021/07/25/react-display-name/</link><pubDate>Sun, 25 Jul 2021 22:33:59 +0800</pubDate><guid>https://en.1991421.cn/2021/07/25/react-display-name/</guid><description>&lt;blockquote>
&lt;p>During development, you may encounter the ESLint error: &amp;ldquo;Component definition is missing display name (react/display-name)&amp;rdquo;. Let&amp;rsquo;s discuss what causes this and how to handle it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="reactdisplay-name">
&lt;a class="heading-anchor-link" href="#reactdisplay-name">react/display-name&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reactdisplay-name"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The only benefit of adding display-name to components is for debugging purposes. As shown in the image below, it helps us quickly locate corresponding components in React developer tools, making it easier to find the source files.&lt;/p>
&lt;p>However, in daily development, we often don&amp;rsquo;t explicitly write display-name. So where do these names come from? React automatically assigns them for us. Generally, components are created in two ways:&lt;/p>
&lt;ol>
&lt;li>Function components: The function name becomes the display-name&lt;/li>
&lt;li>Class components: The class name becomes the display-name&lt;/li>
&lt;/ol>
&lt;p>But sometimes display-name can be missing, such as when we use anonymous functions to create components, or when using &lt;code>React.createElement()&lt;/code>. This creates a burden during development and debugging when trying to quickly locate the rendering component logic. Therefore, from a best practices perspective, &lt;strong>it&amp;rsquo;s recommended to always have react/display-name&lt;/strong>, such as explicitly naming function components.&lt;/p>
&lt;h2 id="custom-display-name">
&lt;a class="heading-anchor-link" href="#custom-display-name">Custom display-name&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-display-name"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned above, React uses function/class names as display-name, but we can also modify this name using &lt;code>componentName.displayName = 'xxxxx'&lt;/code>&lt;/p>
&lt;p>This applies to both classes and functions.&lt;/p>
&lt;p>Note: The name you see in React debugging tools is not the same as the tag name we use when importing components. For example, even if you use &lt;code>as&lt;/code> for aliasing during import, it won&amp;rsquo;t work as expected.&lt;/p>
&lt;p>Here&amp;rsquo;s an example of a class component where we modify the displayName, and you&amp;rsquo;ll see the change reflected in debugging:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">CardToc&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PureComponent&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">CardToc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">displayName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;CardTocCustom&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="eslint-reactdisplay-name">
&lt;a class="heading-anchor-link" href="#eslint-reactdisplay-name">eslint-react/display-name&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslint-reactdisplay-name"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It&amp;rsquo;s recommended to enable this ESLint rule and set it to error level.&lt;/p>
&lt;p>This is our company&amp;rsquo;s practice as well.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#34;react/display-name&amp;#34;: [&amp;#34;error&amp;#34;]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="table-column-render-in-antd-and-other-component-libraries">
&lt;a class="heading-anchor-link" href="#table-column-render-in-antd-and-other-component-libraries">Table column render in antd and other component libraries&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="table-column-render-in-antd-and-other-component-libraries"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned above, we often don&amp;rsquo;t encounter this issue, but it frequently appears when doing custom rendering in table columns. First, this is not antd&amp;rsquo;s fault - for example, Tencent&amp;rsquo;s TEA form components also have this issue. The real reason is that the linter treats these as function components, but they&amp;rsquo;re actually just functions that return ReactNode. Solutions are as follows:&lt;/p>
&lt;ol>
&lt;li>If it&amp;rsquo;s just simple rendering, like hyperlinks, use inline disable: &lt;code>// eslint-disable-next-line react/display-name&lt;/code>&lt;/li>
&lt;li>If it&amp;rsquo;s more complex rendering, extract it as a separate function. This way the linter will recognize it correctly, avoid errors, and improve code readability through render function extraction.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Mark.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md" target="_blank" rel="noopener">https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/ant-desigen/ant-design/issues/26111" target="_blank" rel="noopener">https://github.com/ant-desigen/ant-design/issues/26111&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/yannickcr/eslint-plugin-react/issues/2313" target="_blank" rel="noopener">https://github.com/yannickcr/eslint-plugin-react/issues/2313&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/43356073/how-to-set-displayname-in-a-functional-component-react" target="_blank" rel="noopener">https://stackoverflow.com/questions/43356073/how-to-set-displayname-in-a-functional-component-react&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>JavaScript arguments Object</title><link>https://en.1991421.cn/2021/07/24/js-arguments/</link><pubDate>Sat, 24 Jul 2021 16:58:44 +0800</pubDate><guid>https://en.1991421.cn/2021/07/24/js-arguments/</guid><description>&lt;blockquote>
&lt;p>Due to historical reasons, some functions in project code still use arguments, and the company&amp;rsquo;s lint rules only give warnings for arguments usage, so some haven&amp;rsquo;t been modified.&lt;/p>
&lt;p>So is arguments still necessary? Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="arguments-vs-rest-parameters">
&lt;a class="heading-anchor-link" href="#arguments-vs-rest-parameters">arguments vs rest parameters&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="arguments-vs-rest-parameters"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>arguments is array-like but not a real array, so it lacks methods like map, filter, etc., while rest parameters are actual arrays&lt;/li>
&lt;li>Iterator methods for arguments are not supported in IE&lt;/li>
&lt;li>arguments is from ES3 specification, while rest parameters are from ES6&lt;/li>
&lt;li>rest parameters can be used to get all function parameters and can completely replace arguments&lt;/li>
&lt;/ul>
&lt;p>In conclusion, we can definitely use rest parameters to handle parameter retrieval&lt;/p>
&lt;h2 id="prefer-rest-params">
&lt;a class="heading-anchor-link" href="#prefer-rest-params">prefer-rest-params&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prefer-rest-params"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For legacy code, this rule can be used for detection and fixing. It&amp;rsquo;s better to resolve errors individually.&lt;/p></description></item><item><title>When tsc Compiled to ES3</title><link>https://en.1991421.cn/2021/07/24/tsces3/</link><pubDate>Sat, 24 Jul 2021 16:10:18 +0800</pubDate><guid>https://en.1991421.cn/2021/07/24/tsces3/</guid><description>&lt;p>During a CD deployment our web bundle unexpectedly compiled to ES3, causing a blank screen in browsers.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-07-24-163553.jpeg"
alt="Error screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="root-cause">
&lt;a class="heading-anchor-link" href="#root-cause">Root Cause&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="root-cause"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A shared business component library ignored the main project’s &lt;code>tsconfig&lt;/code> target. Our internal build tool picks the first &lt;code>tsconfig&lt;/code> it finds; the component library lacked a &lt;code>target&lt;/code>, so TypeScript defaulted to ES3. Remember: TypeScript only transpiles syntax—it doesn’t polyfill.&lt;/p>
&lt;h2 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Compile component libraries to JavaScript before publishing. This:&lt;/p>
&lt;ol>
&lt;li>Speeds up app builds (libraries are precompiled).&lt;/li>
&lt;li>Ensures libraries aren’t affected by the host project’s &lt;code>tsconfig&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Lesson learned—always set explicit targets or ship compiled artifacts for shared packages.&lt;/p></description></item><item><title>sideEffects in package.json</title><link>https://en.1991421.cn/2021/07/18/a06267c/</link><pubDate>Sun, 18 Jul 2021 15:50:05 +0800</pubDate><guid>https://en.1991421.cn/2021/07/18/a06267c/</guid><description>&lt;blockquote>
&lt;p>When examining JavaScript projects, you might notice the sideEffects configuration in package.json. What does this mean and how should it be used? Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="sideeffects">
&lt;a class="heading-anchor-link" href="#sideeffects">sideEffects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sideeffects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As the name suggests, side effects refer to functions that perform conditional filtering and return a new array, but also modify the original array itself.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">filterArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filter&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">random&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">item&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">arr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;before:&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filterArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;after:&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The opposite of side effects are pure functions, where fixed input produces fixed output.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">filterArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filter&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">item&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">arr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;before:&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filterArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;after:&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In real life, we also talk about side effects - for example, when we take medicine to cure an illness, but the medicine has some toxicity and may cause headaches, etc. So we can say that program functions with side effects are not purely functional.&lt;/p>
&lt;h2 id="sideeffects-is-not-an-official-npm-standard-field">
&lt;a class="heading-anchor-link" href="#sideeffects-is-not-an-official-npm-standard-field">sideEffects is Not an Official NPM Standard Field&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sideeffects-is-not-an-official-npm-standard-field"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Checking the &lt;a href="https://docs.npmjs.com/cli/v7/configuring-npm/package-json" target="_blank" rel="noopener">npmjs official documentation&lt;/a>, you won&amp;rsquo;t find the sideEffects setting explained because it&amp;rsquo;s not an official standard field. It&amp;rsquo;s a configuration item proposed by webpack to better implement tree shaking (Webpack4+).&lt;/p>
&lt;p>Therefore, tree shaking in webpack requires configuration both in webpack itself and in Package.json.&lt;/p>
&lt;p>sideEffects notifies webpack that the module can be safely tree-shaken without worrying about its side effects.&lt;/p>
&lt;h2 id="sideeffects-values-in-packagejson">
&lt;a class="heading-anchor-link" href="#sideeffects-values-in-packagejson">sideEffects Values in Package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sideeffects-values-in-packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>sideEffects can be false to indicate no side effects, or an array to specify files that do have side effects.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;your-project&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sideEffects&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;./src/some-side-effectful-file.js&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Supports wildcards.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Default value is &lt;code>true&lt;/code>, meaning there are side effects.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Side effect granularity is at the file level.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="webpack-sideeffects-processing-logic">
&lt;a class="heading-anchor-link" href="#webpack-sideeffects-processing-logic">Webpack sideEffects Processing Logic&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack-sideeffects-processing-logic"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>​&lt;/p>
&lt;ul>
&lt;li>If sideEffects is configured as false, all code within the module has no side effects. Any code not imported and used will be deleted, and webpack doesn&amp;rsquo;t need to analyze it&lt;/li>
&lt;li>If sideEffects specifies certain files, webpack will preserve those files during bundling, even if the side-effectful code within them is not used&lt;/li>
&lt;/ul>
&lt;p>Note: Production mode will delete code without side effects that isn&amp;rsquo;t imported, while development mode will retain it but mark it&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/40052192" target="_blank" rel="noopener">How to Use sideEffects in Webpack?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://webpack.js.org/guides/tree-shaking/" target="_blank" rel="noopener">https://webpack.js.org/guides/tree-shaking/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Parsing ZIP File Uploads</title><link>https://en.1991421.cn/2021/07/11/zip-upload-parsing/</link><pubDate>Sun, 11 Jul 2021 15:22:51 +0800</pubDate><guid>https://en.1991421.cn/2021/07/11/zip-upload-parsing/</guid><description>&lt;blockquote>
&lt;p>I recently needed to upload ZIP files: read on the frontend, send to the backend, write to a ZIP file, then extract. During integration, errors indicated the ZIP was invalid.&lt;/p>
&lt;/blockquote>
&lt;p>From the error, the written byte stream was clearly wrong.&lt;/p>
&lt;h2 id="current-code">
&lt;a class="heading-anchor-link" href="#current-code">Current code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I checked the current ZIP read code below.&lt;/p>
&lt;h3 id="frontend">
&lt;a class="heading-anchor-link" href="#frontend">Frontend&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">reader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">FileReader&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readAsText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;UTF-8&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onload&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">evt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setUploadContent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Base64&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">evt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="backend">
&lt;a class="heading-anchor-link" href="#backend">Backend&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="backend"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The backend &lt;code>Go&lt;/code> writing code was roughly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">decContractsSource&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">base64&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">StdEncoding&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">DecodeString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ContractsSourceBase64&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">msg&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Sprintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;base64 DecodeString error %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">seelog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SetBaseResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">apiCommon&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ErrCodeInternalError&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">content&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="nb">byte&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">decContractsSource&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">err&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">ioutil&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WriteFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hellooworld.zip&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">content&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mo">0644&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">msg&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Sprintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;WriteFile error, AppId{%v} %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">AppId&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">seelog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">h&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SetBaseResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">apiCommon&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ErrCodeInternalError&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">msg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The backend just decodes base64 and writes bytes to a ZIP.&lt;/p>
&lt;p>So the likely issue is on the frontend read side.&lt;/p>
&lt;h2 id="about-zip">
&lt;a class="heading-anchor-link" href="#about-zip">About ZIP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="about-zip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Quoted from Wikipedia:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>ZIP&lt;/strong> is an &lt;a href="https://en.wikipedia.org/wiki/Archive_file_format" target="_blank" rel="noopener">archive file format&lt;/a> that supports &lt;a href="https://en.wikipedia.org/wiki/Lossless_compression" target="_blank" rel="noopener">lossless data compression&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>In JS, &lt;code>reader.readAsText&lt;/code> is for text files. ZIP is binary, so reading as text corrupts bytes. That&amp;rsquo;s the bug.&lt;/p>
&lt;h2 id="filereader-apis">
&lt;a class="heading-anchor-link" href="#filereader-apis">FileReader APIs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="filereader-apis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>FileReader.readAsDataURL()&lt;/p>
&lt;ul>
&lt;li>Reads files and base64-encodes them&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>FileReader.readAsText()&lt;/p>
&lt;ul>
&lt;li>Text files&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>FileReader.readAsArrayBuffer()&lt;/p>
&lt;ul>
&lt;li>Binary array&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;del>FileReader.readAsBinaryString() is non-standard and deprecated&lt;/del>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>For ZIPs, do not use &lt;code>readAsText&lt;/code>. Use &lt;code>readAsDataURL&lt;/code> instead, and strip the MIME prefix &lt;code>data:application/zip;base64,&lt;/code>.&lt;/p>
&lt;p>After rewriting, the test passed.&lt;/p>
&lt;h2 id="demo">
&lt;a class="heading-anchor-link" href="#demo">Demo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="demo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>To verify, I made a small &lt;a href="https://github.com/alanhe421/express-demo" target="_blank" rel="noopener">demo&lt;/a>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>FileReader.readAsDataURL()&lt;/code> works for both text and binary (e.g., ZIP) and can fully replace &lt;code>readAsText&lt;/code> here.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Key code blocks:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">reader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">FileReader&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readAsDataURL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">···&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fileContent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">evt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/^(data:[a-z-\/]+;base64,)/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">···&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">···&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// backend
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">buff&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;base64&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`./test.&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fileType&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;zip&amp;#39;&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;zip&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;txt&amp;#39;&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">buff&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">···&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="other-gotchas">
&lt;a class="heading-anchor-link" href="#other-gotchas">Other gotchas&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-gotchas"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I limited uploads to &lt;code>.zip&lt;/code>, but on macOS Chrome, &lt;code>.xlsx&lt;/code> was still accepted — because &lt;code>.xlsx&lt;/code> itself is a ZIP container. Tighten client‑side checks accordingly.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Compression reduces A bytes to B bytes via algorithms; plain text isn’t compressed. Reading binary as text shortens/corrupts content, hence the invalid ZIP.&lt;/li>
&lt;/ul></description></item><item><title>Developing Alfred JS SDK</title><link>https://en.1991421.cn/2021/06/27/alfred-js/</link><pubDate>Sun, 27 Jun 2021 16:02:37 +0800</pubDate><guid>https://en.1991421.cn/2021/06/27/alfred-js/</guid><description>&lt;blockquote>
&lt;p>Since I primarily work in frontend development, I tend to write Alfred workflows in JS. But I find myself repeatedly writing Script Filters, parameter splitting and concatenation, etc. This gave me the idea to develop a JS toolkit, so that common code blocks in future workflows can be directly called, which would definitely improve efficiency quite a bit.&lt;/p>
&lt;p>Better to act than just think about it, let&amp;rsquo;s get started.&lt;/p>
&lt;/blockquote>
&lt;p>Here&amp;rsquo;s the link to my toolkit, &lt;a href="https://www.npmjs.com/package/@stacker/alfred-utils" target="_blank" rel="noopener">click here&lt;/a>. For related development, just install and use directly.&lt;/p>
&lt;p>If you&amp;rsquo;re curious about the source code, visit the GitHub repository address.&lt;/p>
&lt;p>Here are some design considerations during development.&lt;/p>
&lt;h2 id="tech-stack">
&lt;a class="heading-anchor-link" href="#tech-stack">Tech Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tech-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>TypeScript&lt;/li>
&lt;li>NodeJS&lt;/li>
&lt;/ul>
&lt;p>Other things like UT/hooks/CI are all included&lt;/p>
&lt;h2 id="technical-considerations">
&lt;a class="heading-anchor-link" href="#technical-considerations">Technical Considerations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="technical-considerations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>I&amp;rsquo;m writing this using TS-ESModule, and finally compiling to CommonJS with TSC. Since this only serves Alfred, it&amp;rsquo;s definitely a Node environment, so I only output the CommonJS version. Of course, if you want to output an ES version, it&amp;rsquo;s simple - just add a configuration file.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Current IDEs have excellent TS support, so when TSC compiles, I choose to output .d.ts files&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Using standard-version for server version management&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The entire version release uses GitHub Action for automatic publishing, and when publishing is OK, I get a telegram message notification&lt;/p>
&lt;/li>
&lt;li>
&lt;p>To ensure the library&amp;rsquo;s robustness, I added hooks for UT detection&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="some-pitfalls">
&lt;a class="heading-anchor-link" href="#some-pitfalls">Some Pitfalls&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="some-pitfalls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>TSC compilation is incremental compilation, so it&amp;rsquo;s better to rimraf the folder first each time&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Need to understand the differences between .gitignore/.npmignore/Package.json-files, as configuration affects the final published files&lt;/p>
&lt;/li>
&lt;li>
&lt;p>TSC by default compiles all TS files, so you need to explicitly specify which files to include, for example, Test files don&amp;rsquo;t need compilation&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For user convenience when importing, we often add an index file, and the writing style needs attention&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="k">default&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">http&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./http&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="k">default&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">utils&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./utils&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>TypeScript supports writing CommonJS modules directly, but we can also write modules using ES syntax and finally compile to CommonJS, which is the approach used here&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="package-usage">
&lt;a class="heading-anchor-link" href="#package-usage">Package Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm install @stacker/alfred-utils --save
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">utils&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">http&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@stacker/alfred-utils&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">outputScriptFilter&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">items&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filterItemsBy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">items&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">query&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;title&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above is a simple usage of the toolkit.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The toolkit abstracts and encapsulates common operations during development, so that specific usage only requires consumption. Developing workflows in the future can be faster and safer.&lt;/p></description></item><item><title>了解redux-duck</title><link>https://en.1991421.cn/2021/06/20/redux-duck/</link><pubDate>Sun, 20 Jun 2021 18:42:41 +0800</pubDate><guid>https://en.1991421.cn/2021/06/20/redux-duck/</guid><description>&lt;blockquote>
&lt;p>最近参与维护一些项目，其中有用到saga-duck，一脸茫然，于是了解了下，有点收获，这里Mark。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-06-20-234753.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="关联技术点">
&lt;a class="heading-anchor-link" href="#%e5%85%b3%e8%81%94%e6%8a%80%e6%9c%af%e7%82%b9">关联技术点&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="关联技术点"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>saga-duck的学习，延伸出duck，extensible-duck，redux-saga，这些都有必要提下。&lt;/p>
&lt;h3 id="duck">
&lt;a class="heading-anchor-link" href="#duck">Duck&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="duck"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>一种模式/观点&lt;/code>，当使用redux时，我们往往将&lt;code>{actionTypes，actions，reducers}&lt;/code>三种物理分离，实际上很多操作不存在不同业务场景的复用，将其放在一起组织似乎更合理。&lt;/p>
&lt;p>这种思想实际上希望将redux这块的代码进行模块化管理，而非生硬的按照功能类别划分。&lt;/p>
&lt;h3 id="extensible-duck">
&lt;a class="heading-anchor-link" href="#extensible-duck">extensible-duck&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extensible-duck"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>duck模式的一种实现，并且支持redux-saga&lt;/p>
&lt;h3 id="redux-saga">
&lt;a class="heading-anchor-link" href="#redux-saga">redux-saga&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-saga"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>redux-reducer本身只是纯函数，每次操作即一种状态转化为另一种状态，我们固定输入一定即固定了输出，所以reducer都是纯函数。而很多时候会有一些异步操作等，这些操作存在副作用，因此redux-thunk，redux-saga即为了解决这个环节的问题而存在。只是saga更为复杂&amp;amp;强大些。&lt;/p>
&lt;h3 id="saga-duck">
&lt;a class="heading-anchor-link" href="#saga-duck">saga-duck&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="saga-duck"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>saga-duck大部分是借鉴的extensible-duck，官方文档描述是extensible-duck没有考虑支持redux-saga，并且组合使用不太方便，因此才开发了这个工具库。不过extensible-duck已经支持了saga。&lt;/p>
&lt;p>因此extensible-duck/saga-duck等选其一即可。&lt;/p>
&lt;h2 id="saga-duck实践">
&lt;a class="heading-anchor-link" href="#saga-duck%e5%ae%9e%e8%b7%b5">saga-duck实践&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="saga-duck实践"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>官方demo及我这里的&lt;a href="https://github.com/alanhe421/react-demo/blob/d6e17c1cc1eaf421e348b456f6a6f1d9f0ccdd67/src/components/duck-test/test-duck.js#L14-L14" target="_blank" rel="noopener">demo&lt;/a>已经可以说明白，这里不再反复说明。&lt;/p>
&lt;p>我的理解即&lt;/p>
&lt;ol>
&lt;li>单一业务下的数据流转包含副作用处理都可以在一个duck下进行组织处理&lt;/li>
&lt;li>如果有类似的可以做继承/组合进行复用&lt;/li>
&lt;/ol>
&lt;h3 id="遇到的坑">
&lt;a class="heading-anchor-link" href="#%e9%81%87%e5%88%b0%e7%9a%84%e5%9d%91">遇到的坑&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="遇到的坑"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="class-constructor-duck-cannot-be-invoked-without-new">Class constructor Duck cannot be invoked without &amp;rsquo;new'&lt;/h4>&lt;p>如果打包遇到该错误即编译这块的事。saga-duck包JS为ES6，而如果我们项目本身编译后是ES5，因此运行中不会有new，而对于ES6 class不使用new运算符志直接使用则会报以上错误，解决办法可以将saga-duck包含在babel编译的范围内&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sr">/\.(js|jsx|mjs)$/&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">include&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">paths&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">appSrc&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sr">/\/node_modules\/saga-duck/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 关键部分
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;babel-loader&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">configFile&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;./.babelrc.json&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cacheDirectory&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="saga-duck原理">
&lt;a class="heading-anchor-link" href="#saga-duck%e5%8e%9f%e7%90%86">saga-duck原理&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="saga-duck原理"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>duck本身只是提供了类的形式进行redux相关代码组织，整个封装即duck，然后当new一个duck时，会创建redux store。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">duckRuntime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">DuckRuntime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">MyDuck&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">ConnectedC&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">duckRuntime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connectRoot&lt;/span>&lt;span class="p">()(&lt;/span>&lt;span class="nx">DuckTest&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">ConnectedComponent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Provider&lt;/span> &lt;span class="nx">store&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">duckRuntime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">ConnectedC&lt;/span> &lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Provider&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">duck&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">TDuck&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">middlewares&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">any&lt;/span>&lt;span class="p">[]){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">duck&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">duck&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">DuckRuntimeOptions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">middlewares&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">middlewares&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;object&amp;#39;&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">middlewares&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">middlewares&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">middlewares&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">middlewares&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">enhancers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">enhancers&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_initStore&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 创建redux store
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">_initStore&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">duck&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">duck&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">sagaMiddleware&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sagaMiddleware&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createSagaMiddleware&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">enhancer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">compose&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">applyMiddleware&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaMiddleware&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">middlewares&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">enhancers&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createReduxStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">any&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="nx">duck&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reducer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">any&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="nx">enhancer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addSaga&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">duck&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sagas&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="写在最后">
&lt;a class="heading-anchor-link" href="#%e5%86%99%e5%9c%a8%e6%9c%80%e5%90%8e">写在最后&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="写在最后"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>正如开始所说，duck是一种模式，理解这点就不难使用。&lt;/p>
&lt;h2 id="相关文档">
&lt;a class="heading-anchor-link" href="#%e7%9b%b8%e5%85%b3%e6%96%87%e6%a1%a3">相关文档&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="相关文档"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://cyrilluce.gitbook.io/saga-duck/" target="_blank" rel="noopener">https://cyrilluce.gitbook.io/saga-duck/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/erikras/ducks-modular-redux" target="_blank" rel="noopener">https://github.com/erikras/ducks-modular-redux&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Enabling GitHub Two-Factor Authentication</title><link>https://en.1991421.cn/2021/06/14/github/</link><pubDate>Mon, 14 Jun 2021 11:42:06 +0800</pubDate><guid>https://en.1991421.cn/2021/06/14/github/</guid><description>&lt;blockquote>
&lt;p>Recently, I needed to enable two-factor authentication to publish Actions to the GitHub Market, so I set it up. But I found there were still some pitfalls, so I&amp;rsquo;m recording them here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="enabling-github-two-factor-authentication">
&lt;a class="heading-anchor-link" href="#enabling-github-two-factor-authentication">Enabling GitHub Two-Factor Authentication&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enabling-github-two-factor-authentication"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Click on settings-Account security in the top right corner&lt;/li>
&lt;/ol>
&lt;img src="https://static.1991421.cn/2021/2021-06-14-115905.jpeg" style="zoom: 33%;" />
&lt;ol start="2">
&lt;li>
&lt;p>Follow the prompts to enable it. During the process, it will remind you to &lt;code>download recovery codes, download and save them&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The QR code that pops up needs to be scanned with an MFA-supporting app. For authentication apps, there are many choices. I personally prefer using 1Password&lt;/p>
&lt;ul>
&lt;li>Let me explain: the QR code itself is a link, but not necessarily an HTTP link. The reason it&amp;rsquo;s not directly shown as a copyable link is probably because it&amp;rsquo;s insecure and user-unfriendly&lt;/li>
&lt;li>After the app scans it, it stores this link as a login item. Each time you click the app, it actually requests a new, time-valid verification code based on this link&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Fill in the verification code to successfully enable two-factor authentication&lt;/p>
&lt;h3 id="1p-scanning-qr-code">
&lt;a class="heading-anchor-link" href="#1p-scanning-qr-code">1P Scanning QR Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1p-scanning-qr-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The way to find QR code scanning in 1P is quite hidden, as shown in the image&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-06-14-120310.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After scanning the QR code provided by GitHub, 1P can store this item. In future operations, 1P will prompt as follows, just copy and input&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-06-14-120629.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>1P recommends using the web version download rather than the App Store version, as permissions differ&lt;/li>
&lt;/ul>
&lt;h2 id="github-push">
&lt;a class="heading-anchor-link" href="#github-push">GitHub Push&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="github-push"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After enabling two-factor authentication, I suddenly found that code commits weren&amp;rsquo;t working. The solution is: Settings-Developer settings-Personal access tokens, generate a new token as password, input it, and the computer will cache this value, so you can continue normal push operations.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-06-14-121642.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Only after enabling this verification did I notice that 1P actually supports this. I have to say it&amp;rsquo;s really great.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.iplaysoft.com/two-factor-authentication.html" target="_blank" rel="noopener">What is Two-Factor Authentication&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Write a GitHub Action to Count Repo Stars</title><link>https://en.1991421.cn/2021/06/14/github-actionstar/</link><pubDate>Mon, 14 Jun 2021 00:51:26 +0800</pubDate><guid>https://en.1991421.cn/2021/06/14/github-actionstar/</guid><description>&lt;blockquote>
&lt;p>I receive a Telegram notification whenever my OSS projects get a new star — it’s motivating. The automation is powered by GitHub Actions. To improve the experience, I wanted the notification to include the total star count, so I built a reusable Action.&lt;/p>
&lt;/blockquote>
&lt;p>Action marketplace: &lt;a href="https://github.com/marketplace/actions/repo-star-count" target="_blank" rel="noopener">Repo Star Count&lt;/a>. Read on for how it works.&lt;/p>
&lt;h2 id="how-to-get-the-star-count">
&lt;a class="heading-anchor-link" href="#how-to-get-the-star-count">How to get the star count&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-get-the-star-count"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>GitHub Actions doesn’t expose star count as an environment variable, and the star event payload lacks that field. Use the GitHub REST API instead. Key snippet:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl -s &lt;span class="s1">&amp;#39;https://api.github.com/repos/${{ inputs.repoPath }}?page=$i&amp;amp;per_page=100&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> jq .stargazers_count
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;a href="https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md" target="_blank" rel="noopener">jq&lt;/a> is preinstalled on GitHub-hosted runners.&lt;/p>
&lt;h2 id="build-the-github-action">
&lt;a class="heading-anchor-link" href="#build-the-github-action">Build the GitHub Action&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-the-github-action"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To reuse across projects, package it as a composite Action that outputs the star count.&lt;/p>
&lt;h3 id="action源码">
&lt;a class="heading-anchor-link" href="#action%e6%ba%90%e7%a0%81">Action源码&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="action源码"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Repo Star Count&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">summary&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Get count of Github repository stars&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">author&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Alan He&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">inputs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">repoPath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">summary&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Repository Path&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">required&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">default&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{github.repository}}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">outputs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">stars&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">summary&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Repo Stars&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">value&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ steps.repo-stars.outputs.stars }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">runs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">using&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">composite&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">repo-stars&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> STARS=`curl -s &amp;#39;https://api.github.com/repos/${{ inputs.repoPath }}?page=$i&amp;amp;per_page=100&amp;#39; | jq .stargazers_count`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> echo &amp;#34;::set-output name=stars::$STARS&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">shell&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bash&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">branding&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">icon&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;award&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">color&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;green&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="consume-the-action">
&lt;a class="heading-anchor-link" href="#consume-the-action">Consume the Action&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="consume-the-action"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Reference it in your workflow and use the output value:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Star Count&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">repo-stars&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">alanhg/repo-star-count-action@master&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># ${{steps.repo-stars.outputs.stars}} &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once installed, you don’t need to care how the value is fetched — just use the output.&lt;/p>
&lt;h2 id="pitfalls-i-hit">
&lt;a class="heading-anchor-link" href="#pitfalls-i-hit">Pitfalls I hit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pitfalls-i-hit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Docs are thorough, but a few gotchas:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Marketplace listings can lag. If the release succeeded, you can still install the Action.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Workflow error line numbers can be off by one.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Comments in certain places can break scripts:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">id&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">repo-stars&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">|&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># STARS=`curl -s &amp;#39;https://api.github.com/repos/${{ inputs.repoPath }}?page=$i&amp;amp;per_page=100&amp;#39; | jq .stargazers_count`&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">echo &amp;#34;::set-output name=stars::$STARS&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">shell&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bash&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Export values via Action outputs — not via environment variables — so the workflow can consume them.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If it’s repetitive, automate it. Encapsulating the star count logic in an Action hides details and makes it reusable in any repo.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Official docs are still the best source:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://docs.github.com/en/actions/creating-actions" target="_blank" rel="noopener">https://docs.github.com/en/actions/creating-actions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.github.com/en/actions/creating-actions/publishing-actions-in-github-marketplace" target="_blank" rel="noopener">https://docs.github.com/en/actions/creating-actions/publishing-actions-in-github-marketplace&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Online Issue - HTTPS Certificate Error</title><link>https://en.1991421.cn/2021/04/22/https/</link><pubDate>Thu, 22 Apr 2021 22:13:11 +0800</pubDate><guid>https://en.1991421.cn/2021/04/22/https/</guid><description>&lt;blockquote>
&lt;p>Recently, the operations team reported that our production System A login failed. Login failure is a major issue, and the business team almost directly escalated it to P1 - the highest priority bug that must be resolved within 2 hours. The situation was quite tense and serious&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>I went to check and found the prompt indicated an insecure connection. Generally, HTTPS connection insecurity can have two possible causes:&lt;/p>
&lt;ol>
&lt;li>Certificate expired&lt;/li>
&lt;li>Certificate not applicable&lt;/li>
&lt;/ol>
&lt;p>After checking the certificate, I found that indeed, the certificate was for &lt;code>*.lenovomm.com&lt;/code>, but our system was &lt;code>a.lenovo.com&lt;/code>, so it was clearly a certificate error. Finally contacted the relevant responsible person. After diagnosis, it was explained that the issue was caused by certificate expiration and manual update errors, so it was quickly fixed and resolved.&lt;/p>
&lt;p>Although the incident was resolved, there are several key points worth summarizing here. At least similar problems can be avoided in the future, and it&amp;rsquo;s necessary to understand these concepts.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Certificate Validity Period&lt;/p>
&lt;p>HTTPS certificates don&amp;rsquo;t have 10-year validity periods. The maximum validity period is one year, so re-signing is inevitable.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Automated Operations&lt;/p>
&lt;p>Certificate updates are repetitive manual labor. Automation is essential, as manual operations inevitably lead to errors.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>HTTPS Certificate Caching&lt;/p>
&lt;p>When the problem occurred, one colleague said their computer was fine. WHY? Because they still had the previous certificate installed. After all, HTTPS certificate invalidation validation takes time - if you refresh, it will notify you that the new certificate has failed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Wildcard Certificates&lt;/p>
&lt;ul>
&lt;li>A wildcard certificate like &lt;code>*.lenovo.com&lt;/code> will support any second-level domain sites under lenovo, but third-level domains won&amp;rsquo;t be supported and require separate certificate issuance&lt;/li>
&lt;li>Wildcard certificates reduce certificate issuance costs, but if the certificate fails, it could potentially cause problems for more than one site. So it has both advantages and disadvantages, and requires extra caution when using.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Chrome/Firefox HTTPS Security Warnings&lt;/p>
&lt;p>Previously, if it was insecure, you could still continue to access, but now that option is no longer available.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-04-22-222659.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>The == Operator in JavaScript</title><link>https://en.1991421.cn/2021/04/18/js-operators/</link><pubDate>Sun, 18 Apr 2021 10:38:43 +0800</pubDate><guid>https://en.1991421.cn/2021/04/18/js-operators/</guid><description>&lt;blockquote>
&lt;p>I recently saw a question: how do you declare &lt;code>a&lt;/code> so that &lt;code>a == 1 &amp;amp;&amp;amp; a == 2 &amp;amp;&amp;amp; a == 3&lt;/code> returns true? You can tell &lt;code>a&lt;/code> must be an object, but what method should it use? This exposed how weak my understanding was of type coercion with &lt;code>==&lt;/code>, so I reviewed it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="implicit-type-coercion-with-">
&lt;a class="heading-anchor-link" href="#implicit-type-coercion-with-">Implicit type coercion with ==&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implicit-type-coercion-with-"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If types differ, consider primitive and object types:&lt;/p>
&lt;ul>
&lt;li>Primitive types
&lt;ul>
&lt;li>Number and string: string is converted to number&lt;/li>
&lt;li>Number and boolean: boolean is converted to number&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Object types
&lt;ul>
&lt;li>If one is an object, it calls &lt;code>valueOf&lt;/code> and &lt;code>toString&lt;/code> to convert to a primitive&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>If you use === (strict equality), there is no type conversion.&lt;/p>
&lt;h2 id="the-question">
&lt;a class="heading-anchor-link" href="#the-question">The question&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-question"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span> &lt;span class="o">==&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">===&lt;/span>&lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you understand the rules, the solution is straightforward.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">i&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">valueOf&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>This question tests implicit type coercion and is quite interesting.&lt;/li>
&lt;li>In real projects, we usually use strict equality === and enforce it with ESLint to avoid surprises.&lt;/li>
&lt;/ul></description></item><item><title>CDN Explained (Simple Guide)</title><link>https://en.1991421.cn/2021/04/17/cdn-intro/</link><pubDate>Sat, 17 Apr 2021 23:21:15 +0800</pubDate><guid>https://en.1991421.cn/2021/04/17/cdn-intro/</guid><description>&lt;blockquote>
&lt;p>To improve performance, websites can use CDN, but do you really understand CDN? Here we sort out the principles to strengthen understanding.&lt;/p>
&lt;/blockquote>
&lt;h2 id="concept">
&lt;a class="heading-anchor-link" href="#concept">Concept&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concept"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;strong>Content Delivery Network&lt;/strong> (&lt;strong>CDN&lt;/strong>) refers to a computer network system connected to each other through the &lt;a href="https://en.wikipedia.org/wiki/Internet" target="_blank" rel="noopener">Internet&lt;/a> that uses servers closest to each user to send music, pictures, videos, applications, and other files to users more quickly and reliably. This provides high performance, scalability, and low-cost network content delivery to users.&lt;/p>
&lt;/blockquote>
&lt;p>The above is from Wikipedia as a basic concept. Next, let&amp;rsquo;s understand the implementation principle of CDN.&lt;/p>
&lt;h2 id="principle">
&lt;a class="heading-anchor-link" href="#principle">Principle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="principle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-04-17-235549.jpeg"
alt="Understanding CDN - Figure 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>From the above figure, we can see the key technologies implemented by CDN services:&lt;/p>
&lt;ol>
&lt;li>&lt;code>CNAME&lt;/code> - When configuring CDN resources, the domain name configured in the webpage is not the domain name provided by the CDN service, but a custom domain name. This custom domain name actually exists as an alias of the CDN resource domain name.&lt;/li>
&lt;li>CDN services need to have their own &lt;code>DNS&lt;/code> because domain name resolution to IP is required.&lt;/li>
&lt;li>Load balancing capabilities (Global Server Load Balance, abbreviated as &lt;code>GSLB&lt;/code>) - Normally, when we resolve domain names to IPs, we can understand them as static. However, to have load balancing capabilities, the DNS resolution to IP becomes a dynamic process. This allows the CDN to return the most suitable CDN node IP, enabling users to access nearby servers.&lt;/li>
&lt;/ol>
&lt;p>OK, so now we understand CDN.&lt;/p>
&lt;h2 id="dns-prefetch">
&lt;a class="heading-anchor-link" href="#dns-prefetch">DNS-prefetch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dns-prefetch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As we know, domain name to IP resolution takes time. To improve speed, we can use DNS prefetching for these cross-domain static resources in the web. Why does this make it faster? Because Chrome will start a dedicated thread for DNS-prefetching.&lt;/p>
&lt;blockquote>
&lt;p>Generally speaking, reasonable DNS prefetching can bring 50ms ~ 300ms improvement to page performance. Someone has done statistics on this aspect.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//static.1991421.cn&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="benefits-of-cdn">
&lt;a class="heading-anchor-link" href="#benefits-of-cdn">Benefits of CDN&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benefits-of-cdn"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>CDN ultimately provides users with good resource access speed, but in actual use, there are many other benefits besides this. For example, it can also save bandwidth. For non-homologous sites, resources need to be reloaded repeatedly, and our CDN as a third-party resource can also avoid the waste of repeated resource loading.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding not only what it is, but also why it is, makes it interesting and allows us to better master technology.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://tech.youzan.com/dns-prefetching/" target="_blank" rel="noopener">https://tech.youzan.com/dns-prefetching/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Install v8-debug</title><link>https://en.1991421.cn/2021/04/11/v8-debug/</link><pubDate>Sun, 11 Apr 2021 23:27:10 +0800</pubDate><guid>https://en.1991421.cn/2021/04/11/v8-debug/</guid><description>&lt;blockquote>
&lt;p>To understand JavaScript deeply you eventually dive into V8. The &lt;code>v8-debug&lt;/code> shell helps inspect bytecode, optimizations, etc. Here’s how I install it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;del>Some suggest &lt;code>brew install v8&lt;/code>, but that build lacked &lt;code>--print-ast&lt;/code> for me, so I don’t recommend it.&lt;/del>&lt;/p>
&lt;ol>
&lt;li>&lt;code>npm install -g jsvu&lt;/code>&lt;/li>
&lt;li>Run &lt;code>jsvu&lt;/code> and select &lt;strong>V8 debug&lt;/strong>. Re-running &lt;code>jsvu&lt;/code> updates the installed V8 versions.&lt;/li>
&lt;li>Add to &lt;code>.bashrc&lt;/code>/&lt;code>.zshrc&lt;/code>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.jsvu:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">d8&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;v8-debug&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>The alias makes launching the shell easier.&lt;/li>
&lt;/ol></description></item><item><title>Frontend Workflow — Scaffolding</title><link>https://en.1991421.cn/2021/03/29/frontend-workflow-scaffold/</link><pubDate>Mon, 29 Mar 2021 22:42:00 +0800</pubDate><guid>https://en.1991421.cn/2021/03/29/frontend-workflow-scaffold/</guid><description>&lt;blockquote>
&lt;p>In pursuit of DRY (Don&amp;rsquo;t Repeat Yourself) and productivity, there are many things we can do with frontend infrastructure: UI component libraries, custom ESLint rules, shared ESLint configurations, shared CI configurations, etc. These mechanisms can improve frontend work efficiency to some extent, but there&amp;rsquo;s still one link that needs optimization. In work, we don&amp;rsquo;t just have a single project; setting up configurations from scratch for each project is actually very inefficient. For this reason, there are many scaffolding tools in the industry, such as create-react-app and JHipster, but third-party tools are often either too simple or too bloated. It&amp;rsquo;s more convenient to build scaffolding based on your actual needs. So Yeoman comes into play.&lt;/p>
&lt;/blockquote>
&lt;p>What does Yeoman do? THE WEB&amp;rsquo;S SCAFFOLDING TOOL FOR MODERN WEBAPPS.&lt;/p>
&lt;p>Yes, that&amp;rsquo;s exactly what we need. Let&amp;rsquo;s get started.&lt;/p>
&lt;h2 id="configuration-steps">
&lt;a class="heading-anchor-link" href="#configuration-steps">Configuration Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Create a generator project, for example, called &lt;code>generator-react-web&lt;/code> (note the fixed prefix &lt;code>generator-&lt;/code>).&lt;/li>
&lt;li>Create a template project, for example, here I only created a &lt;code>react-web-template&lt;/code>.&lt;/li>
&lt;li>Configure the generator project, mainly to support user-selected configuration options.&lt;/li>
&lt;li>Use &lt;code>npm link&lt;/code> to map local modules for easy testing.&lt;/li>
&lt;li>After everything is okay, publish the package; here I used the npm public registry.&lt;/li>
&lt;li>After success, you can happily start new projects:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ npm install -g generator-react-web.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm install -g @stacker/generator-react-web
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ yo @stacker/react-web
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>For specific configurations, refer to the official website and my &lt;a href="https://github.com/alanhe421/stacker-generator-react-web" target="_blank" rel="noopener">code example&lt;/a>. I won&amp;rsquo;t paste them here.&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>The design I personally recommend is to maintain the generator in a separate repository to provide users with various interactive settings and continuously enrich and improve requirements.&lt;/li>
&lt;li>Template code is maintained in a separate repository, making it cleaner.&lt;/li>
&lt;li>If you don&amp;rsquo;t want to manage multiple repositories, you can also host them in the generator project.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>At this step, we can control and unify more aspects of a project. What else can we do? Yes, for example, creating a custom CLI. Let&amp;rsquo;s do it when the need arises&amp;hellip;&lt;/p></description></item><item><title>JavaScript Prototype Chain Basics</title><link>https://en.1991421.cn/2021/03/24/js-prototype-chain/</link><pubDate>Wed, 24 Mar 2021 20:50:32 +0800</pubDate><guid>https://en.1991421.cn/2021/03/24/js-prototype-chain/</guid><description>&lt;blockquote>
&lt;p>The prototype chain is one of the core JS basics. After reading many articles, it is better to summarize it myself to reinforce understanding.&lt;/p>
&lt;p>Key keywords: prototype, &lt;strong>proto&lt;/strong>, constructor, new.&lt;/p>
&lt;/blockquote>
&lt;p>If there are mistakes, please correct me.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;code>prototype&lt;/code> is a property on functions. &lt;code>__proto__&lt;/code> is a property on objects. &lt;code>constructor&lt;/code> is a property on objects.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>An instance&amp;rsquo;s &lt;code>__proto__&lt;/code> points to the prototype object that created it. The instance prototype&amp;rsquo;s &lt;code>constructor&lt;/code> points to the constructor function. The instance prototype&amp;rsquo;s &lt;code>__proto__&lt;/code> points to the next level up. If there is no other prototype, it is Object&amp;rsquo;s instance prototype.&lt;/p>
&lt;p>See the diagram below (from the web).&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-24-231347.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol start="3">
&lt;li>The constructor exists in two places: the constructor in ES6 class syntax, and the object&amp;rsquo;s constructor property. ES6 class is just syntax sugar; the underlying mechanism is still the prototype chain. So the constructor is the function that creates the instance.&lt;/li>
&lt;li>The prototype chain refers to the chain of objects in inheritance, not functions.&lt;/li>
&lt;/ol>
&lt;h3 id="benefits-of-the-prototype-chain">
&lt;a class="heading-anchor-link" href="#benefits-of-the-prototype-chain">Benefits of the prototype chain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benefits-of-the-prototype-chain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>It is a flexible inheritance model compared to class inheritance. For example, &lt;code>B.prototype = new A()&lt;/code> allows instances of B to access A&amp;rsquo;s methods.&lt;/p>
&lt;h2 id="quiz">
&lt;a class="heading-anchor-link" href="#quiz">Quiz&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="quiz"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here are some questions to test understanding.&lt;/p>
&lt;h3 id="q1">
&lt;a class="heading-anchor-link" href="#q1">Q1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">Fn&lt;/span>&lt;span class="p">(){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">12&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;private getName&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;public getName&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">fn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Fn&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">fn1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Fn&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1, 2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 3, 4, 5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">fn1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">__proto__&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">fn1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">__proto__&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">__proto__&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">Fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getName&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 6, 7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hasOwnProperty&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hasOwnProperty&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">constructor&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">Fn&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* Output
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* private getName
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* true
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* true
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* true
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">* true
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">*/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="q2">
&lt;a class="heading-anchor-link" href="#q2">Q2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">P&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">p1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">P&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">age&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">18&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">constructor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">P&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;zz&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">num&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">P&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">age&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">p1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">p1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">age&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;dd&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">p1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">num&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">p2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">P&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">p2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Output
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 18 dd
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * zz
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="q3">
&lt;a class="heading-anchor-link" href="#q3">Q3&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">test&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">constructor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">constructor&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Output
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * [Function: Function]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding these basic concepts is the foundation of writing good code.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/mqyqingfeng/blog/issues/2" target="_blank" rel="noopener">https://github.com/mqyqingfeng/blog/issues/2&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Upgrading Webpack 4 to 5</title><link>https://en.1991421.cn/2021/03/23/webpack45/</link><pubDate>Tue, 23 Mar 2021 23:18:48 +0800</pubDate><guid>https://en.1991421.cn/2021/03/23/webpack45/</guid><description>&lt;blockquote>
&lt;p>It’s been about five months since Webpack 5 was released, and most of the ecosystem plugins are ready. With some spare time, I started the project upgrade.&lt;/p>
&lt;/blockquote>
&lt;h2 id="key-changes-in-webpack-5">
&lt;a class="heading-anchor-link" href="#key-changes-in-webpack-5">Key Changes in Webpack 5&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-changes-in-webpack-5"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Improved persistent caching for faster builds&lt;/li>
&lt;li>Better tree-shaking, reducing bundle size to some extent&lt;/li>
&lt;li>Several breaking changes to prepare for future evolution&lt;/li>
&lt;/ol>
&lt;p>In short, Webpack is a build tool and won’t affect the runtime functionality of your bundled JS. It’s worth upgrading.&lt;/p>
&lt;h2 id="upgrade-steps">
&lt;a class="heading-anchor-link" href="#upgrade-steps">Upgrade Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrade-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For full details, refer to the official docs. Here are the important steps:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Update Webpack and plugins to the latest versions&lt;/p>
&lt;p>Recommend using &lt;code>yarn upgrade-interactive&lt;/code> to select and update in bulk—more efficient.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Adjust some settings&lt;/p>
&lt;ul>
&lt;li>HtmlWebpackPlugin: the &lt;code>chunksSortMode&lt;/code> option changed; set it to &lt;code>auto&lt;/code> or similar&lt;/li>
&lt;li>&lt;code>webpack-merge&lt;/code>: &lt;code>merge&lt;/code> is no longer the default export&lt;/li>
&lt;li>CLI: use &lt;code>webpack-cli serve&lt;/code> instead of the &lt;code>webpack-dev-server&lt;/code> command&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>If your frontend code depends on Node-specific packages, either remove the dependency or install and configure browser-ready alternatives.&lt;/p>
&lt;ul>
&lt;li>For example, my project used &lt;code>content-disposition&lt;/code>. I rewrote it with browser JS instead.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>The upgrade is fairly straightforward with only a handful of changes, though there are a few gotchas.&lt;/li>
&lt;/ul></description></item><item><title>iframe</title><link>https://en.1991421.cn/2021/03/19/iframe/</link><pubDate>Fri, 19 Mar 2021 18:34:23 +0800</pubDate><guid>https://en.1991421.cn/2021/03/19/iframe/</guid><description>&lt;blockquote>
&lt;p>iframe technology is not obsolete and still has its place, such as in web advertising, Chrome extensions, and other applications. Users may not notice the difference in UI experience, but they are actually interacting with two different site services. Here&amp;rsquo;s a summary of iframe considerations.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>If the iframe contains a same-site page, it shares the parent page&amp;rsquo;s rendering process. Chrome does not start a new process. If it&amp;rsquo;s a different site, a new rendering process is started.&lt;/p>
&lt;ul>
&lt;li>Sharing the same rendering process means that if the child page enters an infinite loop, the parent page will be in a waiting state and become unresponsive.&lt;/li>
&lt;li>It&amp;rsquo;s important to understand two concepts: same-origin means &lt;code>protocol; domain; port are identical&lt;/code>, while same-site means &lt;code>root domain is identical&lt;/code>
&lt;ul>
&lt;li>For example, alan.local page iframe loads a.alan.local page&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>iframe resource loading itself does not block the parent page&amp;rsquo;s HTML parsing and execution, but the parent page&amp;rsquo;s onload event executes after the child page&amp;rsquo;s onload&lt;/p>
&lt;ul>
&lt;li>Need to understand the page lifecycle events:
&lt;ul>
&lt;li>window.onload: The browser has not only loaded HTML but also all external resources: images, styles, etc.&lt;/li>
&lt;li>DOMContentLoaded: The browser has fully loaded HTML and built the DOM tree, but external resources like images and stylesheets may not have finished loading.&lt;/li>
&lt;li>beforeunload/unload: Page destruction and leaving&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>As a cross-origin resource access solution, iframe communication between pages can be categorized into the following two scenarios:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Same-site&lt;/p>
&lt;ul>
&lt;li>Both sites set &lt;code>document.domain = 'alan-test.local';&lt;/code> which allows JavaScript to manipulate page elements, such as child page &lt;code>window.parent.document.getElementById('saveSession')&lt;/code>, and parent page manipulating child page via &lt;code>window.frames['frameId'].document&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Different sites&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Use CDM (cross document messaging) for communication&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;message&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">origin&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s2">&amp;#34;http://example.org:8080&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">targetWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">postMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">targetOrigin&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">transfer&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>iframe remains a valuable technology for specific use cases where embedding external content or creating isolated environments is necessary. Understanding the rendering process sharing, resource loading behavior, and communication mechanisms between same-site and cross-site iframes is crucial for effective implementation. While modern web development often favors alternative approaches for security and performance reasons, iframes continue to serve important roles in web advertising, browser extensions, and legacy system integrations.&lt;/p></description></item><item><title>Commonly Mistaken Export Statements</title><link>https://en.1991421.cn/2021/03/17/export/</link><pubDate>Wed, 17 Mar 2021 21:00:17 +0800</pubDate><guid>https://en.1991421.cn/2021/03/17/export/</guid><description>&lt;blockquote>
&lt;p>ES6 Modules are the official JavaScript module solution. With the popularity of Babel, TypeScript, and SPAs, this technology is very familiar, but sometimes we still make mistakes. Reflecting on the errors, I think the understanding is not thorough enough, so I&amp;rsquo;m marking it down here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-17-210232.jpeg"
alt="Commonly Mistaken Export Statements - Figure 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-17-210258.jpeg"
alt="Commonly Mistaken Export Statements - Figure 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The image shows the syntax error that sometimes occurs.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>First, this error is reported by the TS compiler and is a compilation error, different from a Lint error. A Lint error will tell the specific rule violated, while a compilation error will be &lt;code>ts(code)&lt;/code>, which needs to be distinguished.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The &lt;code>code-1128&lt;/code> in the error is unrelated to the program file we wrote; it&amp;rsquo;s internal to the compiler. Looking at the TypeScript source code, I indeed found the relevant statement. Therefore, if you encounter a TS compilation error that you don&amp;rsquo;t understand, you can use this code to assist in searching for related problems.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Declaration_or_statement_expected&lt;/span>: &lt;span class="kt">diag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1128&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">DiagnosticCategory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">Error&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Declaration_or_statement_expected_1128&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Declaration or statement expected.&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">diag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">code&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">category&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reportsUnnecessary&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">elidedInCompatabilityPyramid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reportsDeprecated&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">code&lt;/span>: &lt;span class="kt">code&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">category&lt;/span>: &lt;span class="kt">category&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>: &lt;span class="kt">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">message&lt;/span>: &lt;span class="kt">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reportsUnnecessary&lt;/span>: &lt;span class="kt">reportsUnnecessary&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">elidedInCompatabilityPyramid&lt;/span>: &lt;span class="kt">elidedInCompatabilityPyramid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reportsDeprecated&lt;/span>: &lt;span class="kt">reportsDeprecated&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Back to the specific error, why is writing it like the above wrong? Because the rules are as follows; let&amp;rsquo;s check MDN again.&lt;/p>
&lt;p>The official export syntax is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Export single feature
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kd">let&lt;/span> &lt;span class="nx">name1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="err">…&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">nameN&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// also var, const
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kd">let&lt;/span> &lt;span class="nx">name1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="err">…&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="err">…&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="err">…&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">nameN&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// also var, const
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">FunctionName&lt;/span>&lt;span class="p">(){...}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">ClassName&lt;/span> &lt;span class="p">{...}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Export list
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">name1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="err">…&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">nameN&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Export previously defined feature as default
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">myFunction&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Export single feature as default
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>When exporting a single object, you need a declaration statement such as let, var, const, function, class, etc., while exporting multiple uses curly braces.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Default exports cannot directly export let, var, or const declarations; the following will cause an error:&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kd">let&lt;/span> &lt;span class="nx">m&lt;/span>&lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Familiarize yourself with basic specifications to program efficiently.&lt;/p></description></item><item><title>Proxy vs. Reflect in JavaScript</title><link>https://en.1991421.cn/2021/03/16/reflect-vs-proxy/</link><pubDate>Tue, 16 Mar 2021 23:38:01 +0800</pubDate><guid>https://en.1991421.cn/2021/03/16/reflect-vs-proxy/</guid><description>&lt;blockquote>
&lt;p>Proxy rarely shows up in day-to-day frontend code, but that doesn’t mean it lacks value. We just don’t reach for it often. Here’s a refresher to keep it in mind.&lt;/p>
&lt;/blockquote>
&lt;h2 id="metaprogramming">
&lt;a class="heading-anchor-link" href="#metaprogramming">Metaprogramming&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="metaprogramming"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MDN groups &lt;code>Proxy&lt;/code> and &lt;code>Reflect&lt;/code> under the metaprogramming umbrella. What does that mean?&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Metaprogramming&lt;/strong> is the practice of writing programs that treat other programs (or themselves) as data—modifying them or performing compile-time work at runtime. Compared with hand-writing every behavior, metaprogramming can boost productivity or provide more flexibility without recompilation.&lt;/p>
&lt;/blockquote>
&lt;p>(Source: Wikipedia.) In other words, metaprogramming is a language-agnostic concept—not unique to JavaScript.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-17-225913.jpeg"
alt="https://static.1991421.cn/2021/2021-03-17-225913.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>(Graphic from &lt;a href="https://draveness.me/metaprogramming/" target="_blank" rel="noopener">https://draveness.me/metaprogramming/&lt;/a>)&lt;/p>
&lt;h2 id="proxy-and-reflect">
&lt;a class="heading-anchor-link" href="#proxy-and-reflect">Proxy and Reflect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="proxy-and-reflect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As the name suggests, &lt;code>Proxy&lt;/code> lets us intercept operations such as &lt;code>get&lt;/code>, &lt;code>set&lt;/code>, or &lt;code>construct&lt;/code> on a target object. Why is that useful? We can transform values, guard access, or observe behavior without touching the original object.&lt;/p>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">firstname&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;alan&amp;#39;&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">userProxy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Proxy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">prop&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="sb">`get prop &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> missing`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">prop&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`set prop &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> as &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userProxy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastname&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// get prop lastname missing
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Accessing a missing property returns a custom message; setting a value logs a message before delegating to the original object. You get a clean interception layer without polluting &lt;code>user&lt;/code> itself.&lt;/p>
&lt;p>Another use case: two-way data binding in form UIs. A proxy can sit between the UI and the underlying state, syncing updates in both directions.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/39179743/what-is-the-difference-between-proxy-constructor-and-reflect" target="_blank" rel="noopener">https://stackoverflow.com/questions/39179743/what-is-the-difference-between-proxy-constructor-and-reflect&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Rashomon511/MyBlog/issues/12" target="_blank" rel="noopener">https://github.com/Rashomon511/MyBlog/issues/12&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Web Security: CSRF</title><link>https://en.1991421.cn/2021/03/16/93186991/</link><pubDate>Tue, 16 Mar 2021 19:53:38 +0800</pubDate><guid>https://en.1991421.cn/2021/03/16/93186991/</guid><description>&lt;blockquote>
&lt;p>CSRF is less famous than XSS but is a common and high-risk vulnerability. Here’s a quick review.&lt;/p>
&lt;/blockquote>
&lt;h2 id="concept">
&lt;a class="heading-anchor-link" href="#concept">Concept&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concept"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>CSRF (Cross-Site Request Forgery) is an attack that forges requests to a server by impersonating a trusted, logged-in user.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-16-224329.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Concepts can be abstract—examples help.&lt;/p>
&lt;p>Suppose you open a phishing email with enticing text like “You’ve won $5,000,000!”. The HTML includes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://test1.com/index?action=delete&amp;amp;id=123&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When the email loads, the &lt;code>&amp;lt;img&amp;gt;&lt;/code> triggers a GET request. If you’re logged in to &lt;code>test1.com&lt;/code>, the browser sends cookies and the request succeeds, deleting the resource with &lt;code>id=123&lt;/code>.&lt;/p>
&lt;p>To avoid this, you switch deletes to POST—but attackers can do this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">form&lt;/span> &lt;span class="na">method&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;POST&amp;#34;&lt;/span> &lt;span class="na">action&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://test1.com/index&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;hidden&amp;#34;&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;action&amp;#34;&lt;/span> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;delete&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;hidden&amp;#34;&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;id&amp;#34;&lt;/span> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;123&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">form&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forms&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">submit&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There are also link-based and XHR-based attacks that rely on user interaction:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://test1.com/index?action=delete&amp;amp;id=123&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 点击就中500万
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These are common CSRF techniques.&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The pattern is: the victim is logged in at site A. While visiting site B, a request is sent to A without the victim’s awareness, performing an unintended action.&lt;/p>
&lt;h2 id="defenses">
&lt;a class="heading-anchor-link" href="#defenses">Defenses&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="defenses"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Given the above, combine multiple defenses wherever possible:&lt;/p>
&lt;ol>
&lt;li>RESTful API design: destructive actions use the proper verbs (e.g., DELETE). Simple links/GETs or bare POSTs can’t trigger them.&lt;/li>
&lt;li>SameSite cookies: set &lt;code>SameSite&lt;/code> on cookies. With &lt;code>Strict&lt;/code>, third-party requests won’t send cookies. With &lt;code>Lax&lt;/code>, only top-level GET navigations send cookies.&lt;/li>
&lt;li>CORS: lock down cross-origin requests to prevent illicit XHRs.&lt;/li>
&lt;li>Referer/Origin checks: e.g., reject requests not originating from your domain(s).&lt;/li>
&lt;li>CSRF tokens: include a per-request or per-session anti-CSRF token that cross-site scripts can’t access.&lt;/li>
&lt;li>For sensitive cookies, set &lt;code>SameSite&lt;/code> as above; with &lt;code>Strict&lt;/code>, no cross-site cookies; with &lt;code>Lax&lt;/code>, only GET navigations send them.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Security is essential for open web systems—take it seriously and apply layered defenses.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/dwqs/blog/issues/68" target="_blank" rel="noopener">https://github.com/dwqs/blog/issues/68&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tech.meituan.com/2018/10/11/fe-security-csrf.html" target="_blank" rel="noopener">https://tech.meituan.com/2018/10/11/fe-security-csrf.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Glossary/CSRF" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Glossary/CSRF&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Web Performance: Reflow and Repaint</title><link>https://en.1991421.cn/2021/03/14/web-perf-reflow-repaint/</link><pubDate>Sun, 14 Mar 2021 17:33:46 +0800</pubDate><guid>https://en.1991421.cn/2021/03/14/web-perf-reflow-repaint/</guid><description>&lt;blockquote>
&lt;p>There are many ways to improve web performance: minify JS, reduce HTTP requests, use CDNs for critical assets, etc. Another lever is reducing reflow/repaint. Here’s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="rendering-pipeline">
&lt;a class="heading-anchor-link" href="#rendering-pipeline">Rendering Pipeline&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rendering-pipeline"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-14-173729.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>From Li Bing’s “Browser Working Principles and Practice”.&lt;/p>
&lt;h2 id="techniques">
&lt;a class="heading-anchor-link" href="#techniques">Techniques&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="techniques"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Toggle CSS classes instead of frequently editing &lt;code>style&lt;/code>
&lt;ul>
&lt;li>Fewer reflow/repaint operations&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Avoid table layouts
&lt;ul>
&lt;li>Smaller reflow/repaint scope&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Batch DOM operations (e.g., &lt;code>DocumentFragment&lt;/code>) or use frameworks like React
&lt;ul>
&lt;li>Fewer reflow/repaint operations&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Debounce &lt;code>window.resize&lt;/code>
&lt;ul>
&lt;li>Fewer reflow/repaint operations&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Separate DOM reads from writes
&lt;ul>
&lt;li>Mixed read/write forces layout flushes&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Use &lt;code>will-change: transform&lt;/code> when appropriate
&lt;ul>
&lt;li>Trade memory for speed&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>At a high level, skipping stages of the pipeline reduces total work and improves performance.&lt;/p></description></item><item><title>Frontend Code Security</title><link>https://en.1991421.cn/2021/03/12/frontend-code-security/</link><pubDate>Fri, 12 Mar 2021 16:52:47 +0800</pubDate><guid>https://en.1991421.cn/2021/03/12/frontend-code-security/</guid><description>&lt;blockquote>
&lt;p>Frontend resources ship to browsers/clients. How do we best protect JS and other assets to reduce risk of code theft in commercial projects? The goal is to raise the barrier and increase the cost of stealing.&lt;/p>
&lt;/blockquote>
&lt;p>Practical measures I’ve used:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Build in production mode; strip debug info.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Minify CSS/HTML/JS.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Add hash to CSS class names.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Minify and mangle JS identifiers (e.g., via terser).&lt;/p>
&lt;ul>
&lt;li>
&lt;p>For example, using Terser&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-12-171756.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Consider code obfuscation where appropriate.&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Adaptive Layout &amp; Responsive Layout</title><link>https://en.1991421.cn/2021/03/11/adaptive-vs-responsive-layout/</link><pubDate>Thu, 11 Mar 2021 11:16:20 +0800</pubDate><guid>https://en.1991421.cn/2021/03/11/adaptive-vs-responsive-layout/</guid><description>&lt;blockquote>
&lt;p>Recently, when discussing the next development of the project with the PM, we talked about mobile web pages and mobile app planning. During this discussion, we mentioned adaptive layout and responsive layout, but in the communication, I felt that these two terms seemed to be always bundled together, as if in everyone&amp;rsquo;s eyes adaptive === responsive === one codebase for all platforms.&lt;/p>
&lt;p>Right? No, as a developer, I often confuse these two terms. Taking advantage of my relatively free time recently, let me organize this.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-11-122414.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Image from &lt;a href="https://www.uxpin.com/studio/blog/responsive-vs-adaptive-design-whats-best-choice-designers/" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;h2 id="adaptive-web-design">
&lt;a class="heading-anchor-link" href="#adaptive-web-design">Adaptive Web Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adaptive-web-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Proposed time: 2010&lt;/li>
&lt;li>Concept: Web design that automatically recognizes screen width and makes corresponding adjustments.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-11-121643.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>Implementation technology: Fixed breakpoints for layout&lt;/li>
&lt;/ul>
&lt;h2 id="responsive-web-design">
&lt;a class="heading-anchor-link" href="#responsive-web-design">Responsive Web Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="responsive-web-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>A subset of adaptive web design&lt;/li>
&lt;li>Concept: All devices use the same set of logic, i.e., the same set of code&lt;/li>
&lt;li>Implementation technology mainly uses CSS media queries to adjust layout for different device types and sizes. Sometimes it also needs some JavaScript assistance, like menus.&lt;/li>
&lt;/ul>
&lt;p>Responsive layout has higher cost compared to adaptive because there are various device types and device resolutions.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://www.alloyteam.com/2015/04/zi-shi-ying-she-ji-yu-xiang-ying-shi-wang-ye-she-ji-qian-tan/" target="_blank" rel="noopener">http://www.alloyteam.com/2015/04/zi-shi-ying-she-ji-yu-xiang-ying-shi-wang-ye-she-ji-qian-tan/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Adaptive_web_design" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Adaptive_web_design&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>JS下的Promise</title><link>https://en.1991421.cn/2021/03/07/promise/</link><pubDate>Sun, 07 Mar 2021 13:33:57 +0800</pubDate><guid>https://en.1991421.cn/2021/03/07/promise/</guid><description>&lt;blockquote>
&lt;p>Many tutorials either oversimplify or overcomplicate Promises. Here I&amp;rsquo;ll organize my understanding to reinforce my knowledge.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-07-151758.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="why-promises-were-created">
&lt;a class="heading-anchor-link" href="#why-promises-were-created">Why Promises Were Created&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-promises-were-created"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Promises were created in the realm of asynchronous programming to solve callback hell.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">A.then(B).then(C)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This syntax is clearly better than nested callbacks.&lt;/p>
&lt;h2 id="promise-usage-notes">
&lt;a class="heading-anchor-link" href="#promise-usage-notes">Promise Usage Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="promise-usage-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Promises have three states: &lt;code>pending&lt;/code>, &lt;code>fulfilled&lt;/code>, and &lt;code>rejected&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Promise.then creates microtasks, and it&amp;rsquo;s important to know that &lt;code>microtasks have higher execution priority than macrotasks&lt;/code>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Microtasks are microscopic tasks, while macrotasks are macroscopic tasks. Tasks initiated by the host environment like window.setTimeout and JS events are macrotasks, but tasks initiated by JS itself like promises are microtasks&lt;/p>
&lt;/li>
&lt;li>
&lt;p>According to the JS event loop and message queue principles, when a macrotask finishes execution, the JS engine checks if there are any unexecuted microtasks within that macrotask. If there are, it continues executing them. Only when all microtasks are completed does it proceed to the next macrotask.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="implementing-a-promise-from-scratch">
&lt;a class="heading-anchor-link" href="#implementing-a-promise-from-scratch">Implementing a Promise from Scratch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementing-a-promise-from-scratch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s a simplified implementation of a Promise&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">Bromise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">executor&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">onResolve_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">onReject_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">onResolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onReject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onResolve_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">onResolve&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onResolve_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">executor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">executor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">demo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Bromise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">executor&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">onResolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">demo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">onResolve&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="asyncawait">
&lt;a class="heading-anchor-link" href="#asyncawait">Async/Await&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="asyncawait"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When discussing Promises, it&amp;rsquo;s natural to continue with Async/Await. While Promises solved callback hell, they still weren&amp;rsquo;t simple enough to write. This led to the creation of Async/Await, which allows asynchronous code to be written in a synchronous style, making code more readable and better aligned with human linear thinking.&lt;/p>
&lt;p>async/await is built on generators and Promises. Generators implement coroutines, allowing generator functions to be paused and resumed.&lt;/p>
&lt;h2 id="event-loopmessage-queue">
&lt;a class="heading-anchor-link" href="#event-loopmessage-queue">Event Loop/Message Queue&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="event-loopmessage-queue"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>JS uses a single-threaded model, meaning it can only execute one thing at a time. However, we sometimes see multiple requests being processed simultaneously in the Networks tab. This is because while JS initiates these requests sequentially, the network process doesn&amp;rsquo;t have just one thread, and Chrome itself uses a multi-process model, allowing multiple requests to be processed concurrently. This doesn&amp;rsquo;t contradict JS&amp;rsquo;s single-threaded model.&lt;/p>
&lt;p>Returning to the topic, the single-threaded nature means only one thing can be done at any moment. So what happens at each moment, and how are all tasks guaranteed to execute? This is where the browser&amp;rsquo;s event loop comes in, which can be simply understood as a while loop. The browser maintains a message queue that stores a bunch of tasks (macrotasks). The queue follows the basic first-in-first-out model, so the main thread of the browser&amp;rsquo;s rendering process executes tasks one by one.&lt;/p>
&lt;p>However, if execution only followed this single task queue, there would be problems. Some asynchronous operations require timeliness, which led to the concept of microtasks. The total number of tasks in the queue remains the same, but individual macrotasks can have additional work added temporarily, hence the concept of microtasks. Microtasks were introduced to address the timeliness requirements of certain operations.&lt;/p>
&lt;p>Of course, there isn&amp;rsquo;t just one message queue. The above describes the most common message queue. There&amp;rsquo;s also a delay queue - when the program encounters timer functions, it places information like callback time and callback function into the delay queue. When the timer expires, that callback function is pushed as a macrotask into the normal message queue.&lt;/p>
&lt;h2 id="two-examples">
&lt;a class="heading-anchor-link" href="#two-examples">Two Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="two-examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on the theoretical knowledge above, let&amp;rsquo;s look at two examples&lt;/p>
&lt;h3 id="q1">
&lt;a class="heading-anchor-link" href="#q1">Q1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">foo&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;foo&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">bar&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;bar start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">foo&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;bar end&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;script start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;setTimeout&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">bar&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;promise executor&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;promise then&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;script end&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Execution process analysis:&lt;/p>
&lt;ol>
&lt;li>First execute console.log(&amp;lsquo;script start&amp;rsquo;); prints &amp;ldquo;script start&amp;rdquo;&lt;/li>
&lt;li>Encounter timer, create a new task and place it in the &lt;code>delay queue&lt;/code>&lt;/li>
&lt;li>Execute bar function. Since bar is marked with async, when entering the function, the JS engine saves the current call stack and other information, then executes console.log(&amp;lsquo;bar start&amp;rsquo;); in bar function, printing &amp;ldquo;bar start&amp;rdquo;&lt;/li>
&lt;li>Next execute await foo(); in bar function. Execute foo function. Since foo is also marked with async, when entering the function, the JS engine saves the current call stack and other information, then executes console.log(&amp;lsquo;foo&amp;rsquo;); in foo function, printing &amp;ldquo;foo&amp;rdquo;&lt;/li>
&lt;li>When executing await foo(), a Promise object is automatically created&lt;/li>
&lt;li>During Promise creation, the resolve() function is called, and the JS engine places this task in the current macrotask&amp;rsquo;s microtask queue&lt;/li>
&lt;li>The JS engine then pauses the current coroutine&amp;rsquo;s execution and returns control of the main thread to the parent coroutine, while also returning the created Promise object to the parent coroutine&lt;/li>
&lt;li>After control returns to the parent coroutine, the parent coroutine calls the then() method of this Promise object to monitor its state changes&lt;/li>
&lt;li>Continue with the parent coroutine&amp;rsquo;s flow, execute new Promise(), print &amp;ldquo;promise executor&amp;rdquo;, call resolve function, and the JS engine adds this task to the end of the microtask queue&lt;/li>
&lt;li>Continue executing the parent coroutine&amp;rsquo;s flow, execute console.log(&amp;lsquo;script end&amp;rsquo;);, printing &amp;ldquo;script end&amp;rdquo;&lt;/li>
&lt;li>The parent coroutine will then finish execution. Before finishing, it enters the microtask checkpoint and executes the microtask queue. There are two microtasks waiting to be executed. Execute the first microtask first, triggering the callback function in the first promise.then(), returning control of the main thread to the bar function&amp;rsquo;s coroutine. After the bar function&amp;rsquo;s coroutine is activated, it continues executing subsequent statements, executing console.log(&amp;lsquo;bar end&amp;rsquo;);, printing &amp;ldquo;bar end&amp;rdquo;&lt;/li>
&lt;li>After the bar function coroutine finishes execution, execute the second microtask in the microtask queue, triggering the callback function in the second promise.then(). After this callback function is activated, execute console.log(&amp;lsquo;promise then&amp;rsquo;);, printing &amp;ldquo;promise then&amp;rdquo;&lt;/li>
&lt;li>After execution completes, return control to the main thread. The current task finishes execution, take the task from the delay queue, execute console.log(&amp;lsquo;setTimeout&amp;rsquo;);, printing &amp;ldquo;setTimeout&amp;rdquo;&lt;/li>
&lt;/ol>
&lt;h2 id="q2">
&lt;a class="heading-anchor-link" href="#q2">Q2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Implement a traffic light that changes a circular div&amp;rsquo;s background color in a cycle: green for 3 seconds, yellow for 1 second, red for 2 seconds&lt;/p>
&lt;p>Here&amp;rsquo;s my implementation code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">lightEl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementsByClassName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">second&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="nx">second&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">trafficLight&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lightEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">backgroundColor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;green&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lightEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">backgroundColor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;yellow&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lightEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">backgroundColor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;red&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">trafficLight&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">main&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Key points:&lt;/p>
&lt;ol>
&lt;li>Use await for pausing&lt;/li>
&lt;li>Use while loop for continuous execution&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://time.geekbang.org/column/intro/216" target="_blank" rel="noopener">Browser Working Principles and Practice - Li Bing&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://time.geekbang.org/column/intro/154" target="_blank" rel="noopener">Re-learning Frontend - winter&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/LuckyWinty/blog/blob/master/markdown/Q%26A/Chrome%E6%B5%8F%E8%A7%88%E5%99%A8setTimeout%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E5%92%8C%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F.md" target="_blank" rel="noopener">https://github.com/LuckyWinty/blog/blob/master/markdown/Q%26A/Chrome%E6%B5%8F%E8%A7%88%E5%99%A8setTimeout%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E5%92%8C%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F.md&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding Promises and the JavaScript event loop is fundamental to writing efficient asynchronous code. While Promises solved the callback hell problem, async/await made asynchronous programming even more intuitive. The key insights are:&lt;/p>
&lt;ul>
&lt;li>Promises provide a cleaner way to handle asynchronous operations compared to nested callbacks&lt;/li>
&lt;li>The event loop with microtasks and macrotasks ensures proper execution order&lt;/li>
&lt;li>Async/await builds on Promises and generators to make asynchronous code read like synchronous code&lt;/li>
&lt;li>Practical examples like the traffic light demonstrate how these concepts work together in real applications&lt;/li>
&lt;/ul>
&lt;p>Mastering these concepts will significantly improve your ability to write clean, efficient JavaScript code.&lt;/p></description></item><item><title>Same-Origin, CSP, and CORS</title><link>https://en.1991421.cn/2021/03/06/cspcors/</link><pubDate>Sat, 06 Mar 2021 15:06:44 +0800</pubDate><guid>https://en.1991421.cn/2021/03/06/cspcors/</guid><description>&lt;blockquote>
&lt;p>These three terms appear frequently in frontend security — here’s a summary. They also connect to related topics like same‑site, cross‑origin, XSS, and CSRF.&lt;/p>
&lt;/blockquote>
&lt;p>They seem varied, but are easier to grasp when tied to real scenarios:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Why the Same‑Origin Policy (SOP)?&lt;/p>
&lt;ul>
&lt;li>“Same origin” means protocol, domain, and port all match. Asynchronous requests from a different origin are cross‑origin. For safety, browsers restrict XHR and similar requests via SOP. Without SOP, site A could freely request resources from site B without user awareness; B could read A’s data or manipulate DOM — unsafe. Note: HTML script and image tags aren’t subject to SOP.&lt;/li>
&lt;li>The same resource requested via XHR (e.g., a script) or fonts loaded via CSS are subject to cross‑origin restrictions.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>SOP isn’t enough — enter CSP&lt;/p>
&lt;ul>
&lt;li>Even with SOP, if a page is injected with malicious scripts, XSS can still occur. Content Security Policy (CSP) narrows allowed sources and types. For example, if CSP only allows external JS, injected inline JS will error and not run.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>SOP + CSP improves safety, but reduces flexibility — enter CORS&lt;/p>
&lt;ul>
&lt;li>Sometimes site A legitimately needs resources from site B, and both trust each other. Cross‑Origin Resource Sharing (CORS) lets the server explicitly authorize allowed origins, enabling safe reuse.&lt;/li>
&lt;li>Alternatives to CORS include reverse proxies, since backend services aren’t subject to SOP. In frontend development, when hitting a non‑local backend (different address), we often configure a proxy to avoid cross‑origin issues.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Same‑site vs. same‑origin&lt;/p>
&lt;ul>
&lt;li>SOP is strict. A company might have many sites like a.111.com and b.111.com — different origins, but often needing limited sharing. “Same‑site” requires only the same eTLD+1. For example, opening B from A may reuse the same renderer process if same‑site and same execution context; B can access A’s window via &lt;code>window.opener&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Hotlink protection via Referer&lt;/p>
&lt;ul>
&lt;li>Since script/img aren’t restricted by SOP, resources can be hotlinked. Servers can use the HTTP Referer header to block unauthorized use (403). Browsers don’t allow arbitrary client‑side modification of Referer, preventing trivial spoofing.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Seen together, these mechanisms evolved to balance security and flexibility. Once you understand the scenarios, they’re straightforward.&lt;/p></description></item><item><title>HTTP Development History</title><link>https://en.1991421.cn/2021/03/03/16e51059/</link><pubDate>Wed, 03 Mar 2021 22:57:32 +0800</pubDate><guid>https://en.1991421.cn/2021/03/03/16e51059/</guid><description>&lt;blockquote>
&lt;p>As a frontend developer, we constantly inspect Network requests. I lacked a systematic review of HTTP, so I compiled one to solidify understanding.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-is-http">
&lt;a class="heading-anchor-link" href="#what-is-http">What is HTTP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-http"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The HyperText Transfer Protocol (HTTP) is an application layer protocol for distributed, collaborative, and hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web. The HTTP protocol was initiated in 1989, and current standards are coordinated and published by the World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF).&lt;/p>
&lt;/blockquote>
&lt;p>Key points&lt;/p>
&lt;ol>
&lt;li>HTTP is a protocol with a history of over 30 years&lt;/li>
&lt;li>HTTP is usually built on top of the TCP/IP protocol, but the HTTP protocol does not specify the layer it must use or support. Therefore, HTTP can be implemented on any Internet protocol or other network.&lt;/li>
&lt;li>HTTP requests are not necessarily initiated from a browser; crawlers or other tools can also send requests.&lt;/li>
&lt;/ol>
&lt;h2 id="versions">
&lt;a class="heading-anchor-link" href="#versions">Versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>HTTP/2 is now mainstream, but it is necessary to understand the development history of the HTTP protocol to better familiarize oneself with it.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;del>HTTP/0.9&lt;/del>&lt;/p>
&lt;ul>
&lt;li>Only accepts GET requests, no version number specified in the request line&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;del>HTTP/1.0&lt;/del>&lt;/p>
&lt;ul>
&lt;li>The first version with a specified version number&lt;/li>
&lt;li>Supports HEAD and POST methods&lt;/li>
&lt;li>Header fields supported, content type of request body, response body status codes, etc.&lt;/li>
&lt;li>Strong caching, If-Modified-Since, Expires&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>HTTP/1.1&lt;/p>
&lt;ul>
&lt;li>Supports GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS methods&lt;/li>
&lt;li>Persistent connections by default
&lt;ul>
&lt;li>Multiple requests can be sent sequentially on a single TCP connection&lt;/li>
&lt;li>Long connections are closed after being idle for a period of time&lt;/li>
&lt;li>Pipelining allows sending requests without waiting for responses, but it’s off by default and rarely used.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Compression/decompression, negotiated caching, and other performance optimization support&lt;/li>
&lt;li>Host header handling&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>HTTP/2&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Multiplexing&lt;/p>
&lt;/li>
&lt;li>
&lt;p>True parallelism on a single connection&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Request prioritization&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Prioritization is determined by the requesting client; for example, browsers control priorities based on the type of requested resource, and this cannot be manually specified by developers.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-09-183303.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Server push&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Header compression&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>HTTP/3 (draft → adoption)&lt;/p>
&lt;ul>
&lt;li>The underlying protocol is changed from TCP to UDP-based QUIC&lt;/li>
&lt;li>Reduces connection establishment time&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-04-202056.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Remember&lt;/p>
&lt;ol>
&lt;li>A large number of websites are still in the process of upgrading from 1.1 to 2.&lt;/li>
&lt;li>Any HTTP protocol, such as HTTP/2 support, requires the web server container (like Nginx) and the access terminal (like Chrome) to support it.
&lt;ul>
&lt;li>For example, HTTP/3 is still a draft. If you want to try it, you need to configure your server to support the protocol and install Chrome Canary with QUIC enabled.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>TLS (HTTPS) is also supported on HTTP/1.1; it does not necessarily require HTTP/2.&lt;/li>
&lt;li>Some websites already support HTTP/3, such as YouTube.
&lt;ul>
&lt;li>To test if a site supports HTTP/3, you can use this tool: &lt;a href="https://gf.dev/http3-test" target="_blank" rel="noopener">https://gf.dev/http3-test&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="http-in-chrome-devtools">
&lt;a class="heading-anchor-link" href="#http-in-chrome-devtools">HTTP in Chrome DevTools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="http-in-chrome-devtools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned at the beginning, front-end developers inevitably encounter many HTTP requests, and the most common tool for viewing requests is Chrome DevTools. With DevTools, we can both learn more about HTTP and understand the mechanisms of how web pages make HTTP requests.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>With HTTP/2 multiplexing, multiple concurrent requests share a single connection, but browsers still cap connections per origin.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>In Chrome, the per‑origin connection cap is typically 6.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Because connections are reused under the same domain name, the TCP connection ID is consistent; this is the connectionID in the Network tab.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-04-002510.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;Canceled&amp;rdquo; often occurs when a navigation interrupts in‑flight requests — a client‑side cancellation, not a server error.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="decrypting-https-for-debugging">
&lt;a class="heading-anchor-link" href="#decrypting-https-for-debugging">Decrypting HTTPS for debugging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="decrypting-https-for-debugging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With Chrome&amp;rsquo;s strong promotion of HTTPS, most websites now use HTTPS. When viewing HTTPS request data in a browser, it&amp;rsquo;s no different from HTTP, but if you want to analyze HTTP requests made from an app, the app itself doesn&amp;rsquo;t provide an interface. In this case, you need a proxy tool to intercept traffic and analyze packets. However, after capturing packets, you&amp;rsquo;ll find a bunch of binary data that&amp;rsquo;s unreadable because of TLS. To view the data as in a browser, you need to install a root CA certificate.&lt;/p>
&lt;p>Since trust anchors come from the OS store, installing your proxy&amp;rsquo;s root CA allows on‑the‑fly TLS decryption. Not all sites permit this (certificate pinning will break it).&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-04-004208.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-04-003501.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Just a few words, serving as a learning review.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE" target="_blank" rel="noopener">https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://imququ.com/post/how-to-decrypt-https.html" target="_blank" rel="noopener">https://imququ.com/post/how-to-decrypt-https.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Connection_management_in_HTTP_1.x" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Connection_management_in_HTTP_1.x&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Confusing splice and slice</title><link>https://en.1991421.cn/2021/03/01/spliceslice/</link><pubDate>Mon, 01 Mar 2021 23:11:54 +0800</pubDate><guid>https://en.1991421.cn/2021/03/01/spliceslice/</guid><description>&lt;blockquote>
&lt;p>As a frontend developer, I&amp;rsquo;ve used these two functions frequently over the past few years but have always confused them. I always need to check the API or TS definitions. The best way is to remember them thoroughly. After all, we remember Marvel heroes clearly. So, I just haven&amp;rsquo;t put in enough effort.&lt;/p>
&lt;p>Here I&amp;rsquo;ll mark down the differences between them for memory.&lt;/p>
&lt;/blockquote>
&lt;h2 id="splice">
&lt;a class="heading-anchor-link" href="#splice">splice&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="splice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="word-meaning">
&lt;a class="heading-anchor-link" href="#word-meaning">Word Meaning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="word-meaning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-01-231952.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&amp;ldquo;Splice&amp;rdquo; means to join together by interweaving.&lt;/p>
&lt;h3 id="function-explanation">
&lt;a class="heading-anchor-link" href="#function-explanation">Function Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="function-explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Array.prototype.splice()&lt;/p>
&lt;blockquote>
&lt;p>The &lt;strong>&lt;code>splice()&lt;/code>&lt;/strong> method changes the contents of an array by removing or replacing existing elements and/or adding new elements &lt;a href="https://en.wikipedia.org/wiki/In-place_algorithm" target="_blank" rel="noopener">in place&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>So, when you need to add, remove, or modify array elements, you can use this method.&lt;/p>
&lt;p>Note that this function will modify the array itself.&lt;/p>
&lt;h2 id="slice">
&lt;a class="heading-anchor-link" href="#slice">slice&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="slice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="word-meaning-1">
&lt;a class="heading-anchor-link" href="#word-meaning-1">Word Meaning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="word-meaning-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-01-232140.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&amp;ldquo;Slice&amp;rdquo; means a thin piece cut from something.&lt;/p>
&lt;h3 id="function-explanation-1">
&lt;a class="heading-anchor-link" href="#function-explanation-1">Function Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="function-explanation-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>String.prototype.slice()&lt;/p>
&lt;blockquote>
&lt;p>The &lt;strong>&lt;code>slice()&lt;/code>&lt;/strong> method extracts a section of a string and returns it as a new string, without modifying the original string.&lt;/p>
&lt;/blockquote>
&lt;p>Array.prototype.slice()&lt;/p>
&lt;blockquote>
&lt;p>The &lt;strong>&lt;code>slice()&lt;/code>&lt;/strong> method returns a shallow copy of a portion of an array into a new array object selected from &lt;code>start&lt;/code> to &lt;code>end&lt;/code> (&lt;code>end&lt;/code> not included) where &lt;code>start&lt;/code> and &lt;code>end&lt;/code> represent the index of items in that array. The original array will not be modified.&lt;/p>
&lt;/blockquote>
&lt;p>So, when you need to extract a subset from a string or array, you can use this method.&lt;/p>
&lt;p>Note that this function will not modify the original string or array. For arrays, slice returns a shallow copy.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>From the above explanation, we can see that splice is only available for arrays, while slice is implemented for both strings and arrays.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>In addition to these two functions, there&amp;rsquo;s also a similar split function, but since it&amp;rsquo;s more commonly used, it&amp;rsquo;s not confusing.&lt;/li>
&lt;li>I have to say that understanding while using them more will help reinforce memory of these boring knowledge points.&lt;/li>
&lt;/ul></description></item><item><title>Array.join vs Template strings vs String concatenation</title><link>https://en.1991421.cn/2021/02/28/array-join-vs-template-strings/</link><pubDate>Sun, 28 Feb 2021 18:01:04 +0800</pubDate><guid>https://en.1991421.cn/2021/02/28/array-join-vs-template-strings/</guid><description>&lt;blockquote>
&lt;p>Recently I saw a question on Stack Overflow, &lt;a href="https://stackoverflow.com/questions/54352451/are-es6-template-literals-faster-than-array-join-at-constructing-long-strings" target="_blank" rel="noopener">Are ES6 template literals faster than Array join at constructing long strings&lt;/a>. This question was directly closed by the moderators, so I couldn&amp;rsquo;t see anyone providing an answer. But the question is indeed interesting, since I wasn&amp;rsquo;t sure about the answer either, I decided to test it and record the results here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="benchmark">
&lt;a class="heading-anchor-link" href="#benchmark">Benchmark&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benchmark"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The test data and code are as follows.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">arr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Array&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;A&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ceil&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">random&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// array.join
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;; &amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// string literal
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">finalString2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reduce&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">; &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// string concatenation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">finalString3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reduce&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;; &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The test results are shown below. As you can see, array.join performs the best. I tried to change the sample data volume (the length of the array), but the results remained consistent.&lt;/p>
&lt;iframe src="https://measurethat.net/Embed?id=167479" width="100%" height="500px">&lt;/iframe>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After some consideration, the reason why string literals and string concatenation consume more performance is that they both need to create temporary resource strings for assignment in the callback functions mentioned above. The difference between string literals and string concatenation is that with string literals, the callback only needs one temporary string, while string concatenation requires two (two &amp;ldquo;+&amp;rdquo; operations).&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Indeed, sometimes performance doesn&amp;rsquo;t matter that much. After all, it&amp;rsquo;s easy for programmers to write code that computers can understand, but writing code that programmers can understand is harder. Sometimes we don&amp;rsquo;t need to worry about these small performance differences. However, when dealing with large volumes of data processing logic, we do need to consider this point, because the system could crash, right?&lt;/p>
&lt;p>From this, at least we know that when dealing with larger data volumes, array.join still offers relatively the best performance.&lt;/p></description></item><item><title>Frontend Performance Optimization Opportunities for the 12306 Homepage</title><link>https://en.1991421.cn/2021/02/27/12306/</link><pubDate>Sat, 27 Feb 2021 22:53:14 +0800</pubDate><guid>https://en.1991421.cn/2021/02/27/12306/</guid><description>&lt;blockquote>
&lt;p>While the most challenging aspect of 12306 is undoubtedly handling massive ticket queries and bookings, the frontend performance also leaves much to be desired. Here is a breakdown of optimization opportunities from a frontend engineering perspective.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>&lt;strong>Minification&lt;/strong>: HTML, CSS, and JS files are currently served without minification. Using build tools like Webpack to strip whitespace and comments would reduce file sizes.&lt;/li>
&lt;li>&lt;strong>Compression&lt;/strong>: Enabling GZIP or Brotli (BR) compression at the server level would further shrink assets, potentially reducing bandwidth overhead by up to two-thirds.&lt;/li>
&lt;li>&lt;strong>Caching Strategy&lt;/strong>: The site currently relies heavily on conditional caching (negotiated cache), with &lt;code>Cache-Control&lt;/code> max-age set to 0. This leads to unnecessary round-trip requests. A mix of strong caching for immutable assets and negotiated caching for dynamic content would be more efficient.&lt;/li>
&lt;li>&lt;strong>HTTP/2&lt;/strong>: Upgrading to HTTP/2 would enable multiplexing, allowing multiple requests to be sent over a single TCP connection, thereby improving loading efficiency.&lt;/li>
&lt;li>&lt;strong>CDN &amp;amp; Domain Sharding&lt;/strong>: Leveraging a Content Delivery Network (CDN) and implementing domain sharding for static assets would reduce latency and bypass browser concurrent connection limits.&lt;/li>
&lt;li>&lt;strong>Bundling&lt;/strong>: Consolidation of multiple JS and CSS files into logical bundles using Webpack or Rollup would significantly decrease the total number of HTTP requests.&lt;/li>
&lt;li>&lt;strong>Lazy Loading&lt;/strong>: Implementing lazy loading for off-screen images and non-critical data requests would improve Initial Page Load time.&lt;/li>
&lt;li>&lt;strong>DNS Prefetching&lt;/strong>: Adding &lt;code>&amp;lt;link rel=&amp;quot;dns-prefetch&amp;quot; href=&amp;quot;//kyfw.12306.cn&amp;quot;&amp;gt;&lt;/code> (and other critical domains) would reduce the latency of initial requests.&lt;/li>
&lt;/ol>
&lt;h2 id="performance-audit">
&lt;a class="heading-anchor-link" href="#performance-audit">Performance Audit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="performance-audit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Running a Lighthouse audit via Chrome DevTools confirms these observations. The site scored around 60—functional, but with significant room for improvement.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-27-225754.jpeg"
alt="12306 Lighthouse Audit Result"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It’s easy to criticize popular applications, but constructive feedback is more valuable. These points highlight how China&amp;rsquo;s &amp;ldquo;National App&amp;rdquo; could provide a smoother experience through standard frontend performance practices.&lt;/p></description></item><item><title>window.opener</title><link>https://en.1991421.cn/2021/02/27/window-opener/</link><pubDate>Sat, 27 Feb 2021 14:51:22 +0800</pubDate><guid>https://en.1991421.cn/2021/02/27/window-opener/</guid><description>&lt;blockquote>
&lt;p>&lt;code>window.opener&lt;/code> isn’t new, but it’s easy to overlook. Here’s why it matters.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-does-windowopener-do">
&lt;a class="heading-anchor-link" href="#what-does-windowopener-do">What does window.opener do?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-does-windowopener-do"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The Window interface’s &lt;code>opener&lt;/code> property returns a reference to the window that opened the window, either with &lt;code>open()&lt;/code>, or by navigating a link with a &lt;code>target&lt;/code> attribute.&lt;/p>
&lt;/blockquote>
&lt;p>In short, &lt;code>opener&lt;/code> lets one window operate on another—within limits.&lt;/p>
&lt;p>Example: Page A opens page B. You want B to trigger a refresh in A when a button is clicked. &lt;code>opener&lt;/code> can help.&lt;/p>
&lt;h2 id="when-does-opener-exist">
&lt;a class="heading-anchor-link" href="#when-does-opener-exist">When does opener exist?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-does-opener-exist"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes &lt;code>window.opener&lt;/code> is &lt;code>null&lt;/code>. Conditions:&lt;/p>
&lt;ol>
&lt;li>Same-site
&lt;ul>
&lt;li>Same-site is looser than same-origin: only protocol and eTLD+1 must match.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>A linking relationship exists
&lt;ul>
&lt;li>A opens B via an anchor, &lt;code>window.open&lt;/code>, or within an iframe.&lt;/li>
&lt;li>If the user types B directly in the address bar, A and B have no relationship.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>If conditions are met, A opens B and B can access &lt;code>window.opener&lt;/code>. They also share the same renderer process.&lt;/p>
&lt;p>Chrome has a UI to inspect processes: three-dots menu → More tools → Task Manager.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-27-150827.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>In this screenshot, two same-site tabs share a process:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-27-151017.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Slight changes to page code may result in separate processes:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-27-151122.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="noopener">
&lt;a class="heading-anchor-link" href="#noopener">noopener&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="noopener"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Sometimes A opens B and they’re same-site, yet separate processes. Why? Special settings.&lt;/p>
&lt;p>Both &lt;code>window.open&lt;/code> and &lt;code>&amp;lt;a&amp;gt;&lt;/code> support &lt;code>noopener&lt;/code> (via &lt;code>rel=&amp;quot;noopener&amp;quot;&lt;/code> for anchors). With &lt;code>noopener&lt;/code>, B opens in a new renderer process and cannot access &lt;code>window.opener&lt;/code>.&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>In Chrome, if you don’t explicitly set &lt;code>rel='opener'&lt;/code> on an &lt;code>&amp;lt;a&amp;gt;&lt;/code>, the browser may open a new process.&lt;/li>
&lt;li>With &lt;code>opener&lt;/code> enabled in a same-site context, &lt;code>sessionStorage&lt;/code> is shared. Use &lt;code>noopener&lt;/code> to avoid this.&lt;/li>
&lt;/ol>
&lt;h2 id="eslint">
&lt;a class="heading-anchor-link" href="#eslint">eslint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For performance and safety, new tabs often prefer &lt;code>noopener&lt;/code>. Enforce it with lint rules:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#39;react/jsx-no-target-blank&amp;#39;: [&amp;#39;error&amp;#39;]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="pros-and-cons-of-sharing-a-renderer-process">
&lt;a class="heading-anchor-link" href="#pros-and-cons-of-sharing-a-renderer-process">Pros and cons of sharing a renderer process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pros-and-cons-of-sharing-a-renderer-process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Pro: shared resources, lower overhead. Con: if tab A crashes, tab B may crash, too. Make a trade-off.&lt;/p></description></item><item><title>Maximum call stack size exceeded in JS</title><link>https://en.1991421.cn/2021/02/24/jsmaximum-call-stack-size-exceeded/</link><pubDate>Wed, 24 Feb 2021 22:32:37 +0800</pubDate><guid>https://en.1991421.cn/2021/02/24/jsmaximum-call-stack-size-exceeded/</guid><description>&lt;blockquote>
&lt;p>In development you may encounter the error &lt;code>Maximum call stack size exceeded&lt;/code>. As the name suggests, it&amp;rsquo;s a stack overflow. But understanding just that is not enough, so here is a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="stack">
&lt;a class="heading-anchor-link" href="#stack">Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A stack is a data structure with LIFO (last in, first out). In JS, function calls can be nested (A calls B calls C). This involves managing multiple execution contexts, and JS engines use a stack.&lt;/p>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">sayA&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sayA&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sayB&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">sayB&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sayB&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sayC&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">sayC&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sayC&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">debugger&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sayA&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When you break in Chrome DevTools, you can see the call stack. When C executes, it is on top, matching stack behavior.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-24-230520.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Since it is a stack, what is the maximum depth? I tested it.&lt;/p>
&lt;h2 id="max-call-stack-depth">
&lt;a class="heading-anchor-link" href="#max-call-stack-depth">Max call stack depth&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="max-call-stack-depth"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">runStack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">runStack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">runStack&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The recursion above causes stack overflow in the browser, but adjusting n avoids errors. This can be used to approximate stack depth.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>In &lt;code>Chrome 88.0.4324.192&lt;/code>, the stack depth is &lt;code>11383&lt;/code>. 11384 triggers the error.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Different browsers and versions have different depths.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Stack depth is not configurable.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The call stack has two metrics: maximum stack capacity and maximum call depth. If either is exceeded, you get overflow. I believe maximum stack capacity refers to memory used by contexts.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>When a stack overflow occurs, the console points to the root error line. But sometimes performance issues occur before errors. How to find expensive functions quickly?&lt;/p>
&lt;h2 id="chrome-devtools-analysis">
&lt;a class="heading-anchor-link" href="#chrome-devtools-analysis">Chrome DevTools analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="chrome-devtools-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The Performance and Memory tools help analyze memory usage.&lt;/p>
&lt;ul>
&lt;li>Performance records memory usage over time.&lt;/li>
&lt;li>Memory can snapshot the call stack at a point.&lt;/li>
&lt;/ul>
&lt;p>Using runStack, Performance shows it executing until overflow.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-24-233144.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>To snapshot before overflow, I set a breakpoint at n=11383. You can see memory usage is high.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-24-234950.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: size units are bytes.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use Chrome DevTools well to better understand the web and troubleshoot efficiently.&lt;/p></description></item><item><title>Percentage margin-top</title><link>https://en.1991421.cn/2021/02/18/margin-top/</link><pubDate>Thu, 18 Feb 2021 20:44:28 +0800</pubDate><guid>https://en.1991421.cn/2021/02/18/margin-top/</guid><description>&lt;blockquote>
&lt;p>The values of margin-top, margin-bottom, padding-top, and padding-bottom support percentages. Those percentages are calculated against the container (i.e., the parent element)’s width, not its height (refer to the containing block’s &lt;a href="https://drafts.csswg.org/css-writing-modes-4/#logical-width" target="_blank" rel="noopener">logical width&lt;/a>). This detail is easy to overlook, so here’s a quick note.&lt;/p>
&lt;/blockquote>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">margin-container&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#214177&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#fff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">600&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">500&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">margin-child&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin-top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#82a6cb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-18-230011.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown, the child’s &lt;code>margin-top&lt;/code> is 60px, i.e., 600 × 10%. &lt;code>padding-top&lt;/code> behaves similarly.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>padding and margin support percentages, but border does not&lt;/li>
&lt;li>the percentage is based on the container width (parent width), not the element’s own width&lt;/li>
&lt;/ul>
&lt;h2 id="why-this-design">
&lt;a class="heading-anchor-link" href="#why-this-design">Why This Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-this-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Beyond remembering this niche detail, it’s worth understanding why it’s designed this way. I checked some references [#Oh Hey, Padding Percentage is Based on the Parent Element’s Width], and the explanation is roughly as follows:&lt;/p>
&lt;ol start="2">
&lt;li>If percentages were based on the container’s height, we’d hit a circular dependency: the container’s height depends on the heights and margins of its children, and if those margins depend on the container’s height, it’s unsolvable. Browser width and height behave differently: width is finite and determinable, while height can be effectively unbounded.&lt;/li>
&lt;li>Basing it on the element itself would violate the broader design consistency. For example, font-size percentages are based on the parent element’s font size.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s my current understanding — corrections are welcome.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://www.hongkiat.com/blog/calculate-css-percentage-margins/" target="_blank" rel="noopener">https://www.hongkiat.com/blog/calculate-css-percentage-margins/&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://drafts.csswg.org/css-box-3/#margins" target="_blank" rel="noopener">https://drafts.csswg.org/css-box-3/#margins&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Choosing Between HashRouter and BrowserRouter on the Web</title><link>https://en.1991421.cn/2021/02/05/webhashrouterbrowserrouter/</link><pubDate>Fri, 05 Feb 2021 18:38:50 +0800</pubDate><guid>https://en.1991421.cn/2021/02/05/webhashrouterbrowserrouter/</guid><description>&lt;blockquote>
&lt;p>In SPA apps, frontend routing typically uses either HashRouter or BrowserRouter. Each has pros and cons. Here’s a summary.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-02-06-110349.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="hashrouter-vs-browserrouter">
&lt;a class="heading-anchor-link" href="#hashrouter-vs-browserrouter">HashRouter vs BrowserRouter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hashrouter-vs-browserrouter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>URL form differs: HashRouter uses URL hashes (e.g., &lt;a href="https://1991421.cn#posts" target="_blank" rel="noopener">https://1991421.cn#posts&lt;/a>) while BrowserRouter uses traditional paths (e.g., &lt;a href="https://1991421.cn/posts%29" target="_blank" rel="noopener">https://1991421.cn/posts)&lt;/a>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>BrowserRouter needs some server setup since users may refresh on a deep URL, which hits the server. Ensure all SPA routes serve the host page. HashRouter doesn’t need this.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;*&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sendFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;/index.html&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>With HashRouter, in-page anchors can complicate things. For a URL like &lt;code>https://1991421.cn/#posts/detail/1234567#comment&lt;/code>, &lt;code>location.hash&lt;/code> is &lt;code>#posts/detail/1234567#comment&lt;/code>; you’ll need to parse it yourself to get the final anchor.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>With HashRouter, the hash portion isn’t included in form submissions.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Both have trade-offs. Choose based on needs.&lt;/li>
&lt;li>If either works, BrowserRouter is generally preferable: no hash-related form/anchor quirks. Just remember the server config.&lt;/li>
&lt;/ul>
&lt;h2 id="how-spa-routing-works">
&lt;a class="heading-anchor-link" href="#how-spa-routing-works">How SPA Routing Works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-spa-routing-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>BrowserRouter uses the HTML5 History API to change URLs and track state.&lt;/p>
&lt;ul>
&lt;li>&lt;code>history.pushState&lt;/code> pushes a new state into the session history and updates the location bar.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>HashRouter uses &lt;code>location.hash&lt;/code> to change URLs and track state.&lt;/p>
&lt;ul>
&lt;li>&lt;code>onhashchange&lt;/code> listens for hash changes.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Note: Both are client-side; URL changes don’t reload the page.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Frontend routers exist to swap content based on the URL. The routing “registration” mechanism is provided by the frontend—nothing more.&lt;/p></description></item><item><title>Contributing to GitHub Projects</title><link>https://en.1991421.cn/2021/01/31/github/</link><pubDate>Sun, 31 Jan 2021 23:07:58 +0800</pubDate><guid>https://en.1991421.cn/2021/01/31/github/</guid><description>&lt;blockquote>
&lt;p>I once read “GitHub brings more than open source—it reshapes how you think.” The gist: don’t just copy frameworks; build something yourself. Struggling through a tool or library forces you to grow. GitHub isn’t just a free buffet—it’s a platform to contribute.&lt;/p>
&lt;/blockquote>
&lt;p>For years I barely contributed—laziness was the main reason. Over the last year I started submitting PRs (ADR, awesome-mac, IDEA plugins, v2ray-docker, Alfred workflows). It cost time but paid off: sharper skills, new friends, better English. Open source gives back.&lt;/p>
&lt;h2 id="contribution-flow">
&lt;a class="heading-anchor-link" href="#contribution-flow">Contribution Flow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="contribution-flow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Standard projects usually have a &lt;code>CONTRIBUTING&lt;/code> doc (see &lt;a href="https://github.com/jaywcjlove/awesome-mac/blob/master/contributing.md" target="_blank" rel="noopener">awesome-mac&lt;/a>). Follow that first. For a typical contributor (not a maintainer), the flow looks like this:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Fork&lt;/strong> the repo.&lt;/li>
&lt;li>&lt;strong>Clone&lt;/strong> your fork locally (GitHub CLI or IDE integration works well).&lt;/li>
&lt;li>Commit and push changes to your fork.&lt;/li>
&lt;li>Open a &lt;strong>pull request&lt;/strong> from your fork to the upstream repo.&lt;/li>
&lt;li>Wait for maintainer review.&lt;/li>
&lt;li>Once merged, your code lands in the main project.&lt;/li>
&lt;/ol>
&lt;h2 id="keep-your-fork-in-sync">
&lt;a class="heading-anchor-link" href="#keep-your-fork-in-sync">Keep Your Fork in Sync&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="keep-your-fork-in-sync"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After a PR lands, your fork doesn’t update automatically. Options:&lt;/p>
&lt;ol>
&lt;li>Delete and re-fork (simple but heavy-handed).&lt;/li>
&lt;li>Rebase your local fork:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git fetch upstream
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git checkout master
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase upstream/master
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push -f origin master
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Most IDEs (e.g., IntelliJ IDEA) provide GUI support for this.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-31-235044.jpeg"
alt="rebase"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="closing-thoughts">
&lt;a class="heading-anchor-link" href="#closing-thoughts">Closing Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closing-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Contribute when you can—it pays dividends while keeping you connected to the community.&lt;/p></description></item><item><title>Improve Frontend Code Quality with Tools — SonarQube</title><link>https://en.1991421.cn/2021/01/28/sonarqube/</link><pubDate>Thu, 28 Jan 2021 15:15:19 +0800</pubDate><guid>https://en.1991421.cn/2021/01/28/sonarqube/</guid><description>&lt;blockquote>
&lt;p>Our backend teams adopted SonarQube; I followed suit on the frontend. Along the way I corrected some misconceptions — notes below.&lt;/p>
&lt;/blockquote>
&lt;h2 id="sonarqube-vs-eslint">
&lt;a class="heading-anchor-link" href="#sonarqube-vs-eslint">SonarQube vs. ESLint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sonarqube-vs-eslint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>ESLint is familiar to frontend devs; it enforces style and some best practices. With unit tests, I once thought Sonar wasn’t necessary. In practice they differ:&lt;/p>
&lt;ol>
&lt;li>ESLint focuses on style and some patterns; you fix findings manually or via autofix.&lt;/li>
&lt;li>Sonar goes beyond style to detect code smells and potential bugs, tracks trends over time, and gives quality gates.&lt;/li>
&lt;/ol>
&lt;p>They’re complementary — especially useful for large teams with varied experience.&lt;/p>
&lt;h2 id="setup">
&lt;a class="heading-anchor-link" href="#setup">Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here’s a minimal setup:&lt;/p>
&lt;ol>
&lt;li>SonarQube Web创建新项目&lt;/li>
&lt;li>项目下增加&lt;code>sonar-project.properties&lt;/code>，按需配置，&lt;/li>
&lt;li>GitLab CI增加如下PipeLine&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sonar:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> stage: sonar_scanner
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> image: &amp;#34;nexus.abc.cn/sonarsource/sonar-scanner-cli:4&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> allow_failure: true
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> services:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - docker:stable-dind
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> script:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - sonar-scanner -Dsonar.projectKey=$SONAR_QUBE_PROJECT_KEY
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> only:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - /^sprint/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This triggers a Sonar scan on branches prefixed with &lt;code>sprint&lt;/code>. Adjust rules and conditions over time.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Humans make mistakes — tools like Sonar complement reviews and tests by continuously surfacing potential issues.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackshare.io/stackups/eslint-vs-sonarqube" target="_blank" rel="noopener">https://stackshare.io/stackups/eslint-vs-sonarqube&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Apply for JetBrains License Using Open-Source Projects</title><link>https://en.1991421.cn/2021/01/27/a98e5e6/</link><pubDate>Wed, 27 Jan 2021 17:56:25 +0800</pubDate><guid>https://en.1991421.cn/2021/01/27/a98e5e6/</guid><description>&lt;blockquote>
&lt;p>Because my company doesn&amp;rsquo;t provide a JB License, and I don&amp;rsquo;t want to use pirated versions due to financial constraints, I have been using my family member&amp;rsquo;s student License. However, this approach requires bothering others every time it&amp;rsquo;s about to expire, so I&amp;rsquo;m unwilling to continue with this method.&lt;/p>
&lt;p>I learned that JB has a support program for open-source project development, which allows free applications for the full suite License. So, I tried to apply, and it worked. Here&amp;rsquo;s a simple record of the application process.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-27-180823.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="license-value">
&lt;a class="heading-anchor-link" href="#license-value">License Value&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="license-value"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, let&amp;rsquo;s understand the specific benefits of this license.&lt;/p>
&lt;ul>
&lt;li>Free use of JetBrains IDE full suite for one year, but not including paid plugins, consulting, and other services. For example, the AI Assistant plugin requires a separate paid subscription. It can be renewed upon expiration.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-04-12-213955.jpeg"
alt="https://static.1991421.cn/2024/2024-04-12-213955.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="application-requirements">
&lt;a class="heading-anchor-link" href="#application-requirements">Application Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="application-requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Open source project &lt;code>[with open source license]&lt;/code>&lt;/li>
&lt;li>The project is &lt;code>active&lt;/code> and has been for &lt;code>more than 6 months&lt;/code>&lt;/li>
&lt;li>You are the project &lt;code>leader&lt;/code> or &lt;code>core developer.&lt;/code>&lt;/li>
&lt;li>The GitHub account has a &lt;code>visible email&lt;/code> that matches the &lt;code>email submitted&lt;/code> in the application to facilitate verification by JB staff.&lt;/li>
&lt;/ol>
&lt;p>For complete official requirements, &lt;a href="https://www.jetbrains.com/zh-cn/community/opensource/#support" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h2 id="applying">
&lt;a class="heading-anchor-link" href="#applying">Applying&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="applying"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Click the application link &lt;a href="https://www.jetbrains.com/shop/eform/opensource?product=ALL" target="_blank" rel="noopener">here&lt;/a> and fill out the form based on facts.&lt;/p>
&lt;p>If you need multiple licenses, simply adjust the quantity, but note that users must be active contributors to the project.&lt;/p>
&lt;p>I applied on the afternoon of January 22nd and received the notification email on the afternoon of the 27th, which is &lt;code>four working days&lt;/code> for review. This review time is for reference only; the official notice is &lt;code>1-2 weeks&lt;/code>.&lt;/p>
&lt;h2 id="usage-restrictions">
&lt;a class="heading-anchor-link" href="#usage-restrictions">Usage Restrictions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-restrictions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>It can be used on multiple devices simultaneously but not with various accounts. For example, if Mac1&amp;rsquo;s account is user1 and Mac2&amp;rsquo;s account is user2, the following error will be reported if they are online simultaneously. If they are all your own devices, setting the account names to be consistent is recommended. For how to modify, refer to &lt;a href="https://github.com/alanhe421/others-note/issues/335" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-05-23-112106.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Valid for one year. You will receive a reminder email when it&amp;rsquo;s close to expiration. Click the link and fill out the application again.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-01-12-213532.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="refreshing-license-in-ide">
&lt;a class="heading-anchor-link" href="#refreshing-license-in-ide">Refreshing License in IDE&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="refreshing-license-in-ide"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When you receive the license email after annual renewal, but the license date in the IDE hasn&amp;rsquo;t been updated, you can manually refresh it using the following method:&lt;/p>
&lt;ol>
&lt;li>Click on activate&lt;/li>
&lt;li>Click on Refresh license&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-01-03-230019.jpeg"
alt="https://static.1991421.cn/2024/2024-01-03-230019.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-01-03-230139.jpeg"
alt="https://static.1991421.cn/2024/2024-01-03-230139.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>I can&amp;rsquo;t help but have more recognition for JB company&amp;rsquo;s mission. They strike a good balance between profit and open source, which can be seen from this License point that the company is not purely for making money.&lt;/li>
&lt;li>From a personal perspective, actively participating in open-source projects allows you to gain technology, make friends, and even get some perks. So, why not keep at it?&lt;/li>
&lt;/ul></description></item><item><title>JavaScript Array Undefined Items</title><link>https://en.1991421.cn/2021/01/27/javascriptundefined/</link><pubDate>Wed, 27 Jan 2021 15:29:58 +0800</pubDate><guid>https://en.1991421.cn/2021/01/27/javascriptundefined/</guid><description>&lt;blockquote>
&lt;p>Recently encountered an issue where array iteration counts were incorrect due to uninitialized elements. This led to the discovery that array iteration counts don&amp;rsquo;t necessarily match the array length. Here&amp;rsquo;s a summary of this pitfall.&lt;/p>
&lt;/blockquote>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">array&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="kc">undefined&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">array&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">array&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">array&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">idx&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">idx&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output result:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-27-153630.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Whether you assign undefined to an array item or leave it uninitialized, the value will be undefined&lt;/p>
&lt;/li>
&lt;li>
&lt;p>During iteration, assigned items will be traversed, but unassigned items are skipped directly&lt;/p>
&lt;p>For example, when you need to control a fixed number of loops, you might write it like this. Note that fill() ensures array items are assigned as undefined. If you omit this, the iteration count will be 0&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Array&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">fill&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">idx&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">idx&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="related-discussions">
&lt;a class="heading-anchor-link" href="#related-discussions">Related Discussions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-discussions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/22448330/javascript-in-operator-for-undefined-elements-in-arrays" target="_blank" rel="noopener">JavaScript &amp;lsquo;in&amp;rsquo; operator for &lt;code>undefined&lt;/code> elements in Arrays&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/38658103/how-does-javascripts-foreach-loop-decide-to-skip-or-iterate-through-undefined" target="_blank" rel="noopener">How does JavaScript&amp;rsquo;s forEach loop decide to skip or iterate through &amp;ldquo;undefined&amp;rdquo; and &amp;ldquo;null&amp;rdquo; elements in an Array?&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Inline JavaScript Event Binding</title><link>https://en.1991421.cn/2021/01/26/javascript/</link><pubDate>Tue, 26 Jan 2021 16:17:08 +0800</pubDate><guid>https://en.1991421.cn/2021/01/26/javascript/</guid><description>&lt;blockquote>
&lt;p>Frameworks are popular and make JS easy to use, but they can also hide fundamentals. Inline JS event binding is less common in modern code, but it’s important to understand.&lt;/p>
&lt;/blockquote>
&lt;h2 id="event-binding-in-react">
&lt;a class="heading-anchor-link" href="#event-binding-in-react">Event binding in React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="event-binding-in-react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React smooths over some details:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Handlers are camelCase (&lt;code>onClick&lt;/code>) vs. native &lt;code>onclick&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Binding handlers across a list doesn’t inherently waste performance — React uses event delegation under the hood. Still, per‑item handlers can hurt readability/maintenance.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Event objects are passed to handlers (name is arbitrary):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Button&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handleClickCancel&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">GlobalI18n.CANCEL&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">private&lt;/span> &lt;span class="nx">handleClickCancel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">callback&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="native-inline-event-binding">
&lt;a class="heading-anchor-link" href="#native-inline-event-binding">Native inline event binding&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="native-inline-event-binding"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Inline usage looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;p&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>selectedIndex: &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">span&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>0&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">span&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">select&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;select&amp;#34;&lt;/span> &lt;span class="na">onchange&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;print(event)&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span> &lt;span class="na">selected&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Option A&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Option B&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Option C&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Option D&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Option E&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">select&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">selectedIndexEle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;p&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">querySelector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;span&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">print&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selectedIndexEle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">innerText&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">selectedIndex&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>
&lt;p>To pass the event inline, you must use the exact name &lt;code>event&lt;/code> (not &lt;code>e&lt;/code>). It refers to &lt;code>window.event&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If you don’t need event specifics (e.g., preventDefault), you can pass &lt;code>this&lt;/code> instead: &lt;code>print(this)&lt;/code>, where &lt;code>this&lt;/code> is the element.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Alternatively, bind in a script block and receive the event explicitly, similar to React.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;select&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;change&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Simple but foundational — worth knowing even if you use frameworks.&lt;/p></description></item><item><title>MySQL Left Join Query Record Count</title><link>https://en.1991421.cn/2021/01/26/mysql/</link><pubDate>Tue, 26 Jan 2021 14:50:19 +0800</pubDate><guid>https://en.1991421.cn/2021/01/26/mysql/</guid><description>&lt;blockquote>
&lt;p>Recently fixed a data issue that involved left joins. In my mind, I thought that A left join B would result in the number of records being the same as table A&amp;rsquo;s record count. However, after actual operation, I discovered my understanding was seriously wrong.&lt;/p>
&lt;/blockquote>
&lt;h2 id="a-diagram-about-left-joins-from-the-internet">
&lt;a class="heading-anchor-link" href="#a-diagram-about-left-joins-from-the-internet">A Diagram About Left Joins from the Internet&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="a-diagram-about-left-joins-from-the-internet"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-26-145404.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Taken from a diagram online, the diagram itself is correct, but I misunderstood it. I thought the record count would still be the same as table A&amp;rsquo;s record count.&lt;/p>
&lt;p>Here&amp;rsquo;s an example to illustrate the problem&lt;/p>
&lt;h2 id="an-example">
&lt;a class="heading-anchor-link" href="#an-example">An Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="an-example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>student table data as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">id,name,address
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1,stu_1,beijing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3,stu_3,dalian
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">22,alan,wuhan
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">55,alan,xinjiang
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>address table records as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">address,id,description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">xinjiang,1,Xinjiang is really beautiful
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">xinjiang,2,Xinjiang is really large
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Left join operation as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">student&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">left&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">join&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">address&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">address&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">address&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The final result set record count is &lt;code>5&lt;/code>. Notice that there are two records for address xinjiang. WHY? Because address xinjiang indeed has two records in the address table.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>W3Schools says this about left joins:&lt;/p>
&lt;blockquote>
&lt;p>The LEFT JOIN keyword returns all records from the left table (table1), and the matched records from the right table (table2). The result is NULL from the right side, if there is no match.&lt;/p>
&lt;/blockquote>
&lt;p>This statement should be improved to clarify that all matching records from table2 will be displayed.&lt;/p>
&lt;p>For the final left join record count, it must be &amp;gt;= table1&amp;rsquo;s record count, because even if no related records are found in table2 for the join condition, they still need to be displayed. But if matches are found and they are not unique, then all these non-unique multiple records will be combined and displayed.&lt;/p>
&lt;p>Of course, for right joins and inner joins, the results are similar when there are multiple matching conditions.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Although the problem is simple, it still needs attention.&lt;/p></description></item><item><title>Instant telegram notification if GitHub is Stared</title><link>https://en.1991421.cn/2021/01/25/githubstar/</link><pubDate>Mon, 25 Jan 2021 23:29:33 +0800</pubDate><guid>https://en.1991421.cn/2021/01/25/githubstar/</guid><description>&lt;blockquote>
&lt;p>I really like the sense of achievement from open source projects. Over the past year, I&amp;rsquo;ve consistently worked on several open source projects and have gained some stars from users, with some even adding me on WeChat to raise requirements - this is the charm of open source.&lt;/p>
&lt;p>For example, when someone stars your project, it would be great to receive Telegram notifications promptly. Since I&amp;rsquo;ve already implemented NPM package release notifications before, this was relatively easy to do, but there were still some pitfalls, so I&amp;rsquo;m documenting them here which might help some friends.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-25-234819.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">watch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">action&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">started&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">REPO_NAME&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ github.event.repository.name }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Notify&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">appleboy/telegram-action@master&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">to&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.TELEGRAM_TO }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.TELEGRAM_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">message&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Someone stars **${{env.REPO_NAME}}** repository, see [here](https://github.com${{github.repository}}).&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">format&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">markdown&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="configuration-instructions">
&lt;a class="heading-anchor-link" href="#configuration-instructions">Configuration Instructions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration-instructions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There are many variables in GitHub Action. &lt;code>${{ github.event.repository.name }}&lt;/code> and &lt;code>${{github.repository}}&lt;/code> are context variables that don&amp;rsquo;t need to be configured - you can use them directly. &lt;code>${{ secrets.TELEGRAM_TO }}&lt;/code> is a repository configuration variable that needs to be set in the repository settings.&lt;/p>
&lt;h3 id="github_server_url-doesnt-work">
&lt;a class="heading-anchor-link" href="#github_server_url-doesnt-work">GITHUB_SERVER_URL Doesn&amp;rsquo;t Work?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="github_server_url-doesnt-work"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The official documentation states that the default environment variable is &lt;code>$GITHUB_SERVER_URL&lt;/code>, and the usage method is &lt;code>$GITHUB_SERVER_URL&lt;/code>. However, if you write it in the message above, it won&amp;rsquo;t work. The reason is that environment variables are used when working with shell commands, but if you need to use the GitHub context in a job step, you need to use the proper context syntax.&lt;/p>
&lt;h3 id="telegram-configuration-parameter-acquisition">
&lt;a class="heading-anchor-link" href="#telegram-configuration-parameter-acquisition">Telegram Configuration Parameter Acquisition&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="telegram-configuration-parameter-acquisition"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>&lt;strong>TELEGRAM_TOKEN&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Create a bot through @BotFather to obtain the token. Note that the complete token format will look like this: &lt;code>12345678:BBFntuCD6nRx1ZIYZ-eCyfP1UO4FeAjnz2M&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>TELEGRAM_TO&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>You need to send a message to the bot to ensure the chat is enabled&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Visit &lt;code>https://api.telegram.org/bot$TELEGRAM_TOKEN/getUpdates&lt;/code> to get the chatID from the response&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As described above, you can implement Telegram notifications. Get started!&lt;/p></description></item><item><title>Optimize Table Performance in Frontend</title><link>https://en.1991421.cn/2021/01/18/frontend-table-performance/</link><pubDate>Mon, 18 Jan 2021 16:19:20 +0800</pubDate><guid>https://en.1991421.cn/2021/01/18/frontend-table-performance/</guid><description>&lt;blockquote>
&lt;p>最近项目面临一个表格性能问题，挺有意思的，这里记录下优化方案&lt;/p>
&lt;/blockquote>
&lt;h2 id="requirements">
&lt;a class="heading-anchor-link" href="#requirements">Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>表格要无限滚动，不要有分页&lt;/li>
&lt;li>表格列多且显示丰富，比如存在可编辑单元格，tooltip，弹窗等&lt;/li>
&lt;li>表格行数较多，最大可达1000行，当前业务数据上看最多只有500行，但考虑长远最大需要支持到1000&lt;/li>
&lt;/ul>
&lt;h2 id="current-behavior">
&lt;a class="heading-anchor-link" href="#current-behavior">Current behavior&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-behavior"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>100行时已出现明显卡顿，滚动不流畅&lt;/li>
&lt;/ul>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>经过分析，决定采用虚拟列表技术来解决该问题，原理即是根据滚动按需渲染视窗内表格行数据。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>当前组件采用的是Antd@v3，查看ChangeLog发现v4已提供了虚拟列表支持，但因此整体升级UI库动力不大，且v4提供的使用方法还是较为啰嗦，因此自行改造解决。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>参考库&lt;a href="https://github.com/wubostc/virtualized-table-for-antd/blob/0.7.4/test/Editable%20Rows.jsx" target="_blank" rel="noopener">virtualized-table-for-antd&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="performance-comparison">
&lt;a class="heading-anchor-link" href="#performance-comparison">Performance comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="performance-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>以一个401行数据的性能表现对比。&lt;/p>
&lt;p>性能检测工具：Chrome Developer Performance&lt;/p>
&lt;h3 id="before-optimization">
&lt;a class="heading-anchor-link" href="#before-optimization">Before optimization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="before-optimization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-18-164740.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="after-optimization">
&lt;a class="heading-anchor-link" href="#after-optimization">After optimization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="after-optimization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-18-164809.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="additionally">
&lt;a class="heading-anchor-link" href="#additionally">Additionally&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="additionally"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>可以采用虚拟列表后，效果还是明显好的，但对于当前我的系统而言，还是较为卡顿。&lt;/p>
&lt;p>原因是表格本身单元格内DOM元素过于庞杂，比如单元格内容中的冗余DOM标签，比如一些模态弹窗等走全局公共的，而非每个单元格一个等等，这些地方也需要优化，这里不再赘述。&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>虚拟列表针对这种业务形态可以很好的优化表格性能，但凡事也有利有弊，比如极限条件下，用户滚动极快，高度就可能会有问题，元素本身也可能会出现卡顿，因为需要快速的读写DOM。&lt;/li>
&lt;li>针对WEB性能分析工具，Chrome Developer中的Performance工具搭配技术框架配套插件如React Developer Tools足矣。&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://react-devtools-tutorial.now.sh/profiling" target="_blank" rel="noopener">https://react-devtools-tutorial.now.sh/profiling&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#flame-chart" target="_blank" rel="noopener">https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.cn/post/6844903727728427022" target="_blank" rel="noopener">https://juejin.cn/post/6844903727728427022&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Difference Between yield and yield*</title><link>https://en.1991421.cn/2021/01/15/yieldyield/</link><pubDate>Fri, 15 Jan 2021 23:45:57 +0800</pubDate><guid>https://en.1991421.cn/2021/01/15/yieldyield/</guid><description>&lt;blockquote>
&lt;p>My understanding of JS generator functions was lacking. I hit a wall recently, so I studied and took notes.&lt;/p>
&lt;/blockquote>
&lt;h2 id="yield-vs-yield">
&lt;a class="heading-anchor-link" href="#yield-vs-yield">yield vs yield*&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="yield-vs-yield"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="yield">
&lt;a class="heading-anchor-link" href="#yield">yield&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="yield"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The yield keyword is used to pause and resume a generator function or legacy generator function.&lt;/p>
&lt;p>The yield keyword is used to pause and resume a generator function (see &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*" target="_blank" rel="noopener">function*&lt;/a> or &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/Legacy_generator_function" target="_blank" rel="noopener">legacy generator functions&lt;/a>).&lt;/p>
&lt;h3 id="yield-1">
&lt;a class="heading-anchor-link" href="#yield-1">yield*&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="yield-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The &lt;strong>&lt;code>yield*&lt;/code> expression&lt;/strong> is used to delegate to another &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*" target="_blank" rel="noopener">&lt;code>generator&lt;/code>&lt;/a> or iterable object.&lt;/p>
&lt;p>The &lt;strong>&lt;code>yield*&lt;/code> expression&lt;/strong> delegates to another &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*" target="_blank" rel="noopener">&lt;code>generator&lt;/code>&lt;/a> or iterable object.&lt;/p>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">sub&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">65&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">70&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nb">String&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fromCharCode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="s1">&amp;#39;begin&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">sub&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="s1">&amp;#39;---------&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">sub&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="s1">&amp;#39;end&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;main end&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="k">of&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-22-164934.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>From the console output we can see:&lt;/p>
&lt;ol>
&lt;li>&lt;code>yield&lt;/code> and &lt;code>yield*&lt;/code> are different.&lt;/li>
&lt;li>&lt;code>yield sub()&lt;/code> returns the sub iterator, while &lt;code>yield* sub()&lt;/code> yields each item from the sub iterator.&lt;/li>
&lt;/ol>
&lt;p>I think of the asterisk as “delegate all”: &lt;code>yield*&lt;/code> delegates each item of the expression, whereas &lt;code>yield&lt;/code> delegates the iterator as a whole.&lt;/p>
&lt;p>Also note: the &lt;code>main&lt;/code> generator has a return value that wasn’t printed. That’s because &lt;code>for...of&lt;/code> cannot access the final return value. If we rewrite it, we can retrieve it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">main&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>![image-20210122170503864](/Users/qhe/Library/Application Support/typora-user-images/image-20210122170503864.png)&lt;/p>
&lt;h2 id="usage-in-reduxsaga">
&lt;a class="heading-anchor-link" href="#usage-in-reduxsaga">Usage in Redux‑Saga&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-in-reduxsaga"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>In practice, I use generators most often in Redux‑Saga effects. In some cases, &lt;code>yield&lt;/code> and &lt;code>yield*&lt;/code> can appear to behave the same.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The official docs’ explanation of the difference is brief — basically just noting they’re different:&lt;/p>
&lt;p>You can use the builtin &lt;code>yield*&lt;/code> operator to compose multiple Sagas in a sequential way. This allows you to sequence your &lt;em>macro-tasks&lt;/em> in a procedural style.&lt;/p>
&lt;p>You can use the built-in &lt;code>yield*&lt;/code> operator to compose multiple Sagas sequentially, which lets you arrange your &lt;em>macro-tasks&lt;/em> in a simple procedural style.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="my-understanding">
&lt;a class="heading-anchor-link" href="#my-understanding">My understanding&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="my-understanding"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>In sagas, most side effects we orchestrate are async (macro‑tasks), so ordering matters.
&lt;ul>
&lt;li>If we must ensure all async in effect A — including async inside effect B that A calls — runs in sequence, use &lt;code>yield*&lt;/code>. If A only delegates to a single B, then &lt;code>yield&lt;/code> vs &lt;code>yield*&lt;/code> often results in the same outcome.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="example-1">
&lt;a class="heading-anchor-link" href="#example-1">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h2 id="with-promises">
&lt;a class="heading-anchor-link" href="#with-promises">With Promises&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="with-promises"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Promises were introduced to solve callback hell in async programming.&lt;/p>
&lt;h2 id="iterators-and-generators">
&lt;a class="heading-anchor-link" href="#iterators-and-generators">Iterators and Generators&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="iterators-and-generators"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://segmentfault.com/q/1010000006657002" target="_blank" rel="noopener">https://segmentfault.com/q/1010000006657002&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/47533738/when-should-i-use-yield-vs-yield-in-redux-saga" target="_blank" rel="noopener">https://stackoverflow.com/questions/47533738/when-should-i-use-yield-vs-yield-in-redux-saga&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://redux-saga.js.org/docs/advanced/SequencingSagas.html" target="_blank" rel="noopener">https://redux-saga.js.org/docs/advanced/SequencingSagas.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Extend AxiosRequestConfig in TypeScript</title><link>https://en.1991421.cn/2021/01/11/typescriptaxiosrequestconfig/</link><pubDate>Mon, 11 Jan 2021 18:13:14 +0800</pubDate><guid>https://en.1991421.cn/2021/01/11/typescriptaxiosrequestconfig/</guid><description>&lt;h2 id="background">
&lt;a class="heading-anchor-link" href="#background">Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Some web requests are slow or require a global loading mask to prevent user interaction. We wanted to add a &lt;code>loading&lt;/code> flag to Axios config so that when &lt;code>loading: true&lt;/code>, the UI shows an indicator for the duration of the request.&lt;/p>
&lt;p>Implementing the behavior is easy, but keeping type safety is trickier because &lt;code>AxiosRequestConfig&lt;/code> doesn’t expose generics for arbitrary properties. The solution: &lt;strong>declaration merging&lt;/strong>.&lt;/p>
&lt;h2 id="code">
&lt;a class="heading-anchor-link" href="#code">Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Create a &lt;code>.d.ts&lt;/code> file and merge the interface:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">declare&lt;/span> &lt;span class="nx">module&lt;/span> &lt;span class="s1">&amp;#39;axios&amp;#39;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">AxiosRequestConfig&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Show a loading spinner while the request is in flight.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loading?&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now editors provide proper hints where &lt;code>loading&lt;/code> is used.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-11-182343.jpeg"
alt="intellisense screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="declaration-merging">
&lt;a class="heading-anchor-link" href="#declaration-merging">Declaration Merging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="declaration-merging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Interfaces with the same name merge their members. Official docs: &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-namespaces" target="_blank" rel="noopener">https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-namespaces&lt;/a>&lt;/p>
&lt;p>Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">address&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span>: &lt;span class="kt">A&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;aaa&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">address&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;bbb&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Declaration merging keeps the customization type-safe. You could cast (&lt;code>as unknown as AxiosRequestConfig&lt;/code>) or wrap Axios, but merging is cleaner.&lt;/li>
&lt;li>Remember: TypeScript augments JavaScript types—it doesn’t change runtime behavior.&lt;/li>
&lt;/ol></description></item><item><title>Frontend Storage Options</title><link>https://en.1991421.cn/2021/01/10/frontend-storage/</link><pubDate>Sun, 10 Jan 2021 12:06:47 +0800</pubDate><guid>https://en.1991421.cn/2021/01/10/frontend-storage/</guid><description>&lt;blockquote>
&lt;p>Web projects need to consider frontend storage solutions due to HTTP statelessness, frontend performance, and data sharing when SPA application pages jump. Each solution has its own characteristics, and understanding them is essential for reasonable selection and use. Here is a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="storage-solutions">
&lt;a class="heading-anchor-link" href="#storage-solutions">Storage Solutions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="storage-solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>cookie&lt;/li>
&lt;li>sessionStorage&lt;/li>
&lt;li>localStorage&lt;/li>
&lt;li>indexedDB&lt;/li>
&lt;/ul>
&lt;h3 id="web-sql">
&lt;a class="heading-anchor-link" href="#web-sql">&lt;del>Web SQL&lt;/del>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web-sql"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>You can still see Web SQL in Chrome browser developer tools, but it is not part of the HTML5 specification and is not supported by Firefox, so it is &lt;a href="https://zh.wikipedia.org/wiki/Web_SQL_%E6%95%B0%E6%8D%AE%E5%BA%93" target="_blank" rel="noopener">not recommended&lt;/a>.&lt;/p>
&lt;h2 id="solution-differences">
&lt;a class="heading-anchor-link" href="#solution-differences">Solution Differences&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution-differences"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For the above solutions, here are the differences from various dimensions.&lt;/p>
&lt;h3 id="storage-size">
&lt;a class="heading-anchor-link" href="#storage-size">Storage Size&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="storage-size"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>cookie: 4kb&lt;/li>
&lt;li>sessionStorage: 2.5-10MB, varies by browser&lt;/li>
&lt;li>localStorage: 2.5-10MB, varies by browser&lt;/li>
&lt;li>indexedDB: &amp;gt; 250MB, varies by browser&lt;/li>
&lt;/ul>
&lt;h3 id="expiration-time">
&lt;a class="heading-anchor-link" href="#expiration-time">Expiration Time&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="expiration-time"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Cookies with an expiration time are automatically cleared when they expire&lt;/li>
&lt;li>sessionStorage is cleared when the browser is closed
&lt;ul>
&lt;li>Note that different tabs and windows have different sessions. However, if you copy a page A directly in the browser, you will find that the sessionStorage default value is from the previous page, but it is still an independent session storage.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>localStorage is permanently stored&lt;/li>
&lt;li>indexedDB is permanently stored&lt;/li>
&lt;/ul>
&lt;h3 id="interaction-with-server">
&lt;a class="heading-anchor-link" href="#interaction-with-server">Interaction with Server&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="interaction-with-server"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Cookies have interaction; sessionStorage, localStorage, and indexedDB do not
&lt;ul>
&lt;li>Cookies are carried in every request [except cross-domain asynchronous requests], so do not make cookie data too heavy, which can cause large request packets&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-10-234644.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="synchronous-vs-asynchronous">
&lt;a class="heading-anchor-link" href="#synchronous-vs-asynchronous">Synchronous vs Asynchronous&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="synchronous-vs-asynchronous"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>indexedDB operations are asynchronous; cookie, sessionStorage, and localStorage are all synchronous&lt;/li>
&lt;/ul>
&lt;h3 id="access-policy">
&lt;a class="heading-anchor-link" href="#access-policy">Access Policy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="access-policy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>cookie, sessionStorage, indexedDB, and localStorage comply with the &lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy" target="_blank" rel="noopener">Same-Origin Policy&lt;/a> and can be accessed.&lt;/li>
&lt;li>Cookies can be shared across different origins by setting a domain.&lt;/li>
&lt;/ul>
&lt;h2 id="application-scenarios">
&lt;a class="heading-anchor-link" href="#application-scenarios">Application Scenarios&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="application-scenarios"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>cookie: Login tokens, short-term storage&lt;/li>
&lt;li>sessionStorage: Short-term storage without communication with the backend and size requirements&lt;/li>
&lt;li>localStorage: Long-term storage&lt;/li>
&lt;li>indexedDB: Large amounts of structured data, long-term storage&lt;/li>
&lt;/ul></description></item><item><title>Jenkins Continuous Integration - Publishing New NPM Packages</title><link>https://en.1991421.cn/2021/01/07/jenkins-npm/</link><pubDate>Thu, 07 Jan 2021 14:54:19 +0800</pubDate><guid>https://en.1991421.cn/2021/01/07/jenkins-npm/</guid><description>&lt;blockquote>
&lt;p>Recently there was a requirement to implement NPM package publishing with Jenkins. Here I&amp;rsquo;m documenting the configuration method.&lt;/p>
&lt;/blockquote>
&lt;h2 id="specific-requirements">
&lt;a class="heading-anchor-link" href="#specific-requirements">Specific Requirements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="specific-requirements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Execute release command to generate new version numbers and changelog records&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Submit modified package.json and changelog to GitLab repository&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Package new version to private Nexus server&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="jenkins-plugin-installation">
&lt;a class="heading-anchor-link" href="#jenkins-plugin-installation">Jenkins Plugin Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jenkins-plugin-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To achieve this functionality, ensure the following 3 plugins are installed:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://plugins.jenkins.io/nodejs/" target="_blank" rel="noopener">NodeJS&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://plugins.jenkins.io/ssh-agent/" target="_blank" rel="noopener">SSH Agent&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://plugins.jenkins.io/config-file-provider/" target="_blank" rel="noopener">Config File Provider&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="environment-configuration">
&lt;a class="heading-anchor-link" href="#environment-configuration">Environment Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Install NodeJS&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Install specified NodeJS in Global Tool Configuration, install yarn as global package&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-04-30-165912.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Add NRM configuration to config file&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Select Npm config file in Managed Files, pay attention to auth config to ensure packages can be deployed to the server&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-04-30-170717.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>GitLab credentialsId&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Configure GitLab account private key in Global credentials&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-04-30-165845.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="pipeline-configuration">
&lt;a class="heading-anchor-link" href="#pipeline-configuration">Pipeline Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pipeline-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">node&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">nodejs&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">nodeJSInstallationName:&lt;/span> &lt;span class="s2">&amp;#34;nodejs_v10.16.0&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">configId:&lt;/span> &lt;span class="s2">&amp;#34;96de5baa-02b1-4cb9-9f65-e8f96452b59c&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">stage&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s2">&amp;#34;checkout&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">checkout&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="n">$class&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;GitSCM&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">branches:&lt;/span> &lt;span class="o">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nl">name:&lt;/span> &lt;span class="s1">&amp;#39;master&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">],&lt;/span> &lt;span class="nl">userRemoteConfigs:&lt;/span> &lt;span class="o">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nl">credentialsId:&lt;/span> &lt;span class="s1">&amp;#39;gitlab&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">url:&lt;/span> &lt;span class="s1">&amp;#39;git@gitlab.1991421.cn:personal/hello-web.git&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">]])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">stage&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s2">&amp;#34;release lib&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sshagent&lt;/span>&lt;span class="o">(&lt;/span> &lt;span class="o">[&lt;/span>&lt;span class="s1">&amp;#39;gitlab&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sh&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> git config --global user.email &amp;#34;alan@1991421.cn&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> git config --global user.name &amp;#34;Alan&amp;#39;s 2st Bot&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> yarn install
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> npm run release
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> git push origin HEAD:refs/heads/master --tags
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>File download (&lt;a href="https://gist.github.com/alanhe421/fcdf6daeb9ded0e5427578f5aafdf023" target="_blank" rel="noopener">&lt;strong>jenkins-release-npm.groovy&lt;/strong>&lt;/a>)&lt;/p>
&lt;h3 id="field-value-explanation">
&lt;a class="heading-anchor-link" href="#field-value-explanation">Field Value Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="field-value-explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>nodeJSInstallationName&lt;/code> is the specified version name in Global Tool Configuration-NodeJS&lt;/li>
&lt;li>&lt;code>nodejs configId&lt;/code> is the npmrc file ID in Config File Management&lt;/li>
&lt;li>&lt;code>userRemoteConfigs credentialsId&lt;/code> is the Git repository private token ID in Credentials&lt;/li>
&lt;/ul>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Adding &lt;code>--tags&lt;/code> when pushing upstream has two reasons:
&lt;ul>
&lt;li>The release command uses &lt;code>standard-version&lt;/code> for version management behind the scenes. Generating ChangeLog requires tags, otherwise duplicate commit display issues occur in ChangeLog&lt;/li>
&lt;li>Tags serve as version snapshots, beneficial for version management and issue fixing&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>npm package caching is not considered here. It&amp;rsquo;s recommended to optimize this point when time permits, otherwise reinstalling third-party stable version packages each time wastes resources&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>I&amp;rsquo;ve always felt that Jenkins documentation and plugins are uneven in quality, but at the same time, because Jenkins is indeed designed reasonably and cleverly, it still counts as a mainstream CI/CD tool, which is both amusing and frustrating. When actually implementing the above requirements, I still encountered some pitfalls, so I&amp;rsquo;m documenting this here as a memo&lt;/li>
&lt;li>With the above configuration, package version management is essentially handed over to Jenkins for automation. For developers, they only need to follow specifications to submit feat, fix, etc.&lt;/li>
&lt;/ul></description></item><item><title>Applying for a Wildcard Certificate</title><link>https://en.1991421.cn/2021/01/01/wildcard-ssl-certificate/</link><pubDate>Fri, 01 Jan 2021 16:31:49 +0800</pubDate><guid>https://en.1991421.cn/2021/01/01/wildcard-ssl-certificate/</guid><description>&lt;blockquote>
&lt;p>Recently, I set up my personal English blog with the domain en.1991421.cn. My existing Chinese blog uses 1991421.cn, and my image hosting uses static.1991421.cn. Applying for separate single-domain certificates for each domain would be too troublesome to maintain. To simplify management and make it easier to expand with new subdomain services in the future, I decided to apply for a wildcard certificate.&lt;/p>
&lt;/blockquote>
&lt;h2 id="wildcard-certificates">
&lt;a class="heading-anchor-link" href="#wildcard-certificates">Wildcard Certificates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wildcard-certificates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A wildcard certificate is a certificate that serves multiple subdomains under a single domain. For example, the *.1991421.cn certificate I&amp;rsquo;m applying for can be used on any subdomain like en.1991421.cn or static.1991421.cn.&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>*.1991421.cn can only be used on second-level subdomains; it won&amp;rsquo;t work on the top-level domain 1991421.cn&lt;/p>
&lt;/li>
&lt;li>
&lt;p>A certificate for 1991421.cn can only be used on 1991421.cn&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If you install a single-domain certificate for 1991421.cn on en.1991421.cn, browsers will show a security warning indicating the certificate is invalid&lt;/p>
&lt;p>​&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-01-172150.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="certificate-request">
&lt;a class="heading-anchor-link" href="#certificate-request">Certificate request&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="certificate-request"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here I use Certbot to apply for a Let&amp;rsquo;s Encrypt free certificate. For the specific execution script, you can refer to &lt;a href="https://github.com/alanhe421/alanhe421.github.io/blob/master/deploy/init-letsencrypt.sh" target="_blank" rel="noopener">here&lt;/a>.&lt;/p>
&lt;p>The main code for certificate application is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker-compose run --rm --entrypoint &lt;span class="s2">&amp;#34;\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> certbot certonly -w /var/www/certbot \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &lt;/span>&lt;span class="nv">$staging_arg&lt;/span>&lt;span class="s2"> \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &lt;/span>&lt;span class="nv">$email_arg&lt;/span>&lt;span class="s2"> \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> --manual --preferred-challenges=dns \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &lt;/span>&lt;span class="nv">$domain_args&lt;/span>&lt;span class="s2"> \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> --rsa-key-size &lt;/span>&lt;span class="nv">$rsa_key_size&lt;/span>&lt;span class="s2"> \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> --agree-tos \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> --force-renewal&amp;#34;&lt;/span> certbot
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>During the certificate application process, the terminal will prompt you to add a DNS TXT record. Follow the instructions to add the corresponding DNS record on your domain registrar&amp;rsquo;s website.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-01-165634.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;img src="https://static.1991421.cn/2021/2021-10-31-153807.jpeg" style="zoom:67%;" />
&lt;p>Once the DNS records are configured, the terminal will continue executing. If you see &amp;ldquo;Congratulations&amp;rdquo;, it means the application was successful. Use the certificate according to the file path provided in the output.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-01-170646.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="notes-1">
&lt;a class="heading-anchor-link" href="#notes-1">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;details>&lt;summary>Wildcard certificates can only use DNS validation, so you need to manually add DNS TXT records. If you configure the wrong validation type, you'll see this error:&lt;/summary>
Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA. You may need to use an authenticator plugin that can do challenges over DNS.
&lt;/details>
&lt;details>&lt;summary>The validation method must be unique. If you specify both webroot and manual in your script, you'll see this error:&lt;/summary>
Too many flags setting configurators/installers/authenticators 'webroot' -> 'manual'
&lt;/details>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>DNS validation is more troublesome than webroot validation, but this is only for the first time. After that, you can use Certbot for automatic renewal&lt;/li>
&lt;li>Once you have a wildcard certificate, you don&amp;rsquo;t need to apply for separate certificates for any new second-level subdomain services, which is much more convenient&lt;/li>
&lt;/ul>
&lt;h2 id="automating-wildcard-certificate-renewal">
&lt;a class="heading-anchor-link" href="#automating-wildcard-certificate-renewal">Automating Wildcard Certificate Renewal&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="automating-wildcard-certificate-renewal"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You can use Certbot to renew certificates. It provides hooks that can be combined with DNS provider APIs to automatically add TXT records for complete automation.&lt;/p>
&lt;ol>
&lt;li>Installation&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">git clone https://github.com/alanhe421/certbot-letencrypt-wildcardcertificates-alydns-au.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cd certbot-letencrypt-wildcardcertificates-alydns-au
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod 0777 au.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Configuration&lt;/li>
&lt;/ol>
&lt;p>Edit &lt;strong>domain.ini&lt;/strong> to add your DNS authentication information.&lt;/p>
&lt;ol start="3">
&lt;li>First Manual Application&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">certbot&lt;/span> &lt;span class="n">certonly&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">1991421.&lt;/span>&lt;span class="n">cn&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">manual&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">preferred&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">challenges&lt;/span> &lt;span class="n">dns&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">manual&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">auth&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hook&lt;/span> &lt;span class="s2">&amp;#34;/var/www/certbot-letencrypt-wildcardcertificates-alydns-au/au.sh python txy add&amp;#34;&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">manual&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cleanup&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hook&lt;/span> &lt;span class="s2">&amp;#34;/var/www/certbot-letencrypt-wildcardcertificates-alydns-au/au.sh python txy clean&amp;#34;&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="err">@&lt;/span>&lt;span class="n">myemail&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After successful application, you can use &lt;code>certbot certificates&lt;/code> to view the applied certificates.&lt;/p>
&lt;ol start="4">
&lt;li>Crontab Automation&lt;/li>
&lt;/ol>
&lt;p>For complete information, see &lt;a href="https://github.com/alanhe421/certbot-letencrypt-wildcardcertificates-alydns-au" target="_blank" rel="noopener">https://github.com/alanhe421/certbot-letencrypt-wildcardcertificates-alydns-au&lt;/a>&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Wildcard_certificate" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Wildcard_certificate&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://letsencrypt.org/docs/faq/" target="_blank" rel="noopener">https://letsencrypt.org/docs/faq/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Frontend Basics — P2</title><link>https://en.1991421.cn/2020/12/27/frontend-basics-p2/</link><pubDate>Sun, 27 Dec 2020 17:21:01 +0800</pubDate><guid>https://en.1991421.cn/2020/12/27/frontend-basics-p2/</guid><description>&lt;blockquote>
&lt;p>More notes from frontend interview discussions to reinforce basics.&lt;/p>
&lt;/blockquote>
&lt;h2 id="mvvm">
&lt;a class="heading-anchor-link" href="#mvvm">MVVM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mvvm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MVVM is an architectural pattern. For context, compare common patterns:&lt;/p>
&lt;h3 id="mvc">
&lt;a class="heading-anchor-link" href="#mvc">MVC&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mvc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Model–view–controller&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>Model — implements domain logic and data management.&lt;/li>
&lt;li>View — UI and its presentation.&lt;/li>
&lt;li>Controller — routes requests and handles input.&lt;/li>
&lt;/ul>
&lt;h3 id="mvp">
&lt;a class="heading-anchor-link" href="#mvp">MVP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mvp"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Model–view–presenter&lt;/p>
&lt;/blockquote>
&lt;p>In MVC, Model and View may interact. In MVP, the Presenter mediates between Model and View, typically via interfaces.&lt;/p>
&lt;h3 id="mvvm-1">
&lt;a class="heading-anchor-link" href="#mvvm-1">MVVM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mvvm-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Model–view–viewmodel&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>Model — domain model or data access layer.&lt;/li>
&lt;li>View — the UI users see (structure, layout, appearance).&lt;/li>
&lt;li>ViewModel — an abstraction of the view exposing properties and commands. Instead of a Controller/Presenter, MVVM commonly relies on binding between View and ViewModel.&lt;/li>
&lt;/ul>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>React is not MVVM; it’s essentially ui = render(data).&lt;/li>
&lt;li>Vue isn’t strictly MVVM but borrows many ideas.&lt;/li>
&lt;li>Angular is MVC.&lt;/li>
&lt;/ul>
&lt;h2 id="how-browsers-work">
&lt;a class="heading-anchor-link" href="#how-browsers-work">How browsers work&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-browsers-work"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-204808.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="notes-1">
&lt;a class="heading-anchor-link" href="#notes-1">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Persistent connections: TCP isn’t closed after one HTTP request unless &lt;code>Connection: close&lt;/code>.&lt;/li>
&lt;li>One TCP connection can carry multiple HTTP requests.&lt;/li>
&lt;li>With HTTP/2, multiple requests can run in parallel over one TCP connection.&lt;/li>
&lt;li>TCP connections may be kept alive; a refresh may not renegotiate TLS.&lt;/li>
&lt;li>Browsers limit concurrent TCP connections per host (e.g., Chrome ~6).&lt;/li>
&lt;/ol>
&lt;h2 id="react-fiber">
&lt;a class="heading-anchor-link" href="#react-fiber">React Fiber&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-fiber"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>React Fiber是对核心算法的一次重新实现&lt;/p>
&lt;/blockquote>
&lt;p>From v16, Fiber is the new reconciler. The goal is smoother updates under heavy UI by slicing work into chunks and yielding back to the browser between units to avoid blocking rendering.&lt;/p>
&lt;p>Diagrams below highlight differences between v15 and v16 with Fiber:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-223821.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-223835.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="how-redux-updates-components">
&lt;a class="heading-anchor-link" href="#how-redux-updates-components">How Redux updates components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-redux-updates-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Essentially, the &lt;code>connect&lt;/code> HOC subscribes components to the store; on changes, Redux triggers updates (e.g., via component.forceUpdate under the hood).&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-232051.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="further-reading">
&lt;a class="heading-anchor-link" href="#further-reading">Further reading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="further-reading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/Performance/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" target="_blank" rel="noopener">渲染页面：浏览器的工作原理&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/acdlite/react-fiber-architecture" target="_blank" rel="noopener">React Fiber Architecture&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/80655889" target="_blank" rel="noopener">带着问题看React-Redux源码&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Frontend Basics — P1</title><link>https://en.1991421.cn/2020/12/27/frontend-basics-p1/</link><pubDate>Sun, 27 Dec 2020 17:17:29 +0800</pubDate><guid>https://en.1991421.cn/2020/12/27/frontend-basics-p1/</guid><description>&lt;blockquote>
&lt;p>Recently discussed some frontend interview questions with friends; summarizing here to solidify fundamentals.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tcp-three-way-handshake">
&lt;a class="heading-anchor-link" href="#tcp-three-way-handshake">TCP three-way handshake&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tcp-three-way-handshake"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understand the three-way handshake and four-way FIN sequence. Mnemonic aside, here are the diagrams:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-151107.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-151346.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="number-precision">
&lt;a class="heading-anchor-link" href="#number-precision">Number precision&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="number-precision"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mf">0.2&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="why">
&lt;a class="heading-anchor-link" href="#why">Why&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Decimal &lt;code>0.1&lt;/code> and &lt;code>0.2&lt;/code> are converted to binary; many decimals are repeating in base‑2, causing precision issues.&lt;/p>
&lt;h3 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use a library like big.js for decimal math.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">Big&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.1&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">plus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.2&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="frontend-local-storage-options">
&lt;a class="heading-anchor-link" href="#frontend-local-storage-options">Frontend local storage options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-local-storage-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>localstorage&lt;/li>
&lt;li>sessionStorage&lt;/li>
&lt;li>cookie&lt;/li>
&lt;/ul>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>localStorage entries have no built‑in expiration.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>sessionStorage does not persist across tabs.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Browsers don’t expose a delete‑cookie API; delete by setting an expired date.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Storage values are strings. When storing objects, serialize/deserialize explicitly; otherwise JS will stringify implicitly.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;alan&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// alan
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;age&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;age&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// string
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;alan&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">age&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="p">});&lt;/span> &lt;span class="c1">// [object Object]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;null&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// null
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;undefined&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="hoisting">
&lt;a class="heading-anchor-link" href="#hoisting">Hoisting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hoisting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>In JS, declarations of functions and variables are hoisted to the top of their scope.&lt;/li>
&lt;li>Variables can be used before their declaration.&lt;/li>
&lt;li>Only declarations are hoisted; initializations are not.&lt;/li>
&lt;li>function, var, let, const, and class are all hoisted; &lt;code>var&lt;/code> initializes to &lt;code>undefined&lt;/code>, while &lt;code>let/const&lt;/code> remain uninitialized (TDZ).&lt;/li>
&lt;/ul>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// source code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Hello World!&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// transformed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Hello World!&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">helloWorld&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// TypeError: helloWorld is not a function
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">helloWorld&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Hello World!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="closure">
&lt;a class="heading-anchor-link" href="#closure">Closure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>A function bundled with references to its surrounding state (&lt;strong>lexical environment&lt;/strong>) is a &lt;strong>closure&lt;/strong>. In other words, closures allow an inner function to access the scope of its outer function. In JavaScript, a closure is created whenever a function is created.&lt;/p>
&lt;/blockquote>
&lt;h3 id="example-1">
&lt;a class="heading-anchor-link" href="#example-1">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">btn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementsByTagName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;button&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">btn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">btn&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">onclick&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// always 5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can use a closure, like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">btn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementsByTagName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;button&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">btn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">btn&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">onclick&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nx">j&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">j&lt;/span>&lt;span class="p">))(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="asynchronous-execution-order">
&lt;a class="heading-anchor-link" href="#asynchronous-execution-order">Asynchronous Execution Order&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="asynchronous-execution-order"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;strong>Asynchronous&lt;/strong> means two or more objects or events do &lt;strong>not&lt;/strong> exist or occur at the same time (or the occurrence of multiple related things does not need to wait for the completion of the previous thing). In computer technology, the term &amp;ldquo;asynchronous&amp;rdquo; is used in two main contexts (networking and communication, software design).&lt;/p>
&lt;/blockquote>
&lt;h3 id="notes-1">
&lt;a class="heading-anchor-link" href="#notes-1">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Promises are asynchronous&lt;/li>
&lt;li>XHR can be synchronous or asynchronous&lt;/li>
&lt;li>window.setTimeout is asynchronous&lt;/li>
&lt;/ul>
&lt;h3 id="example-2">
&lt;a class="heading-anchor-link" href="#example-2">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">async1&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;async1 start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">await&lt;/span> &lt;span class="nx">async2&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;async1 end&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">async2&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;async2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;script start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;setTimeout&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">async1&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;promise1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;promise2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;script end&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Answer:&lt;/p>
&lt;ul>
&lt;li>script start&lt;/li>
&lt;li>async1 start&lt;/li>
&lt;li>async2&lt;/li>
&lt;li>promise1&lt;/li>
&lt;li>script end&lt;/li>
&lt;li>async1 end&lt;/li>
&lt;li>promise2&lt;/li>
&lt;li>setTimeout&lt;/li>
&lt;/ul>
&lt;h2 id="decode-url-encoded-multiple-times">
&lt;a class="heading-anchor-link" href="#decode-url-encoded-multiple-times">Decode URL Encoded Multiple Times&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="decode-url-encoded-multiple-times"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://baike.baidu.com/item/%25E8%2583%25A1%25E6%25AD%258C/312718&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">do&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">decodeURI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="nb">decodeURI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="css-box-model">
&lt;a class="heading-anchor-link" href="#css-box-model">CSS Box Model&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="css-box-model"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>When laying out a document, the browser&amp;rsquo;s rendering engine represents all elements as rectangular boxes according to the &lt;strong>CSS basic box model&lt;/strong>. CSS determines the size, position, and properties (such as color, background, border size, etc.) of these boxes.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-27-164811.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="example-3">
&lt;a class="heading-anchor-link" href="#example-3">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">box&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">red&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="kc">solid&lt;/span> &lt;span class="kc">blue&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The width of the red area is 120px, because &lt;code>100px + 10px + 10px&lt;/code>&lt;/p>
&lt;h2 id="three-major-css-features">
&lt;a class="heading-anchor-link" href="#three-major-css-features">Three Major CSS Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="three-major-css-features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Cascade
&lt;ul>
&lt;li>Proximity principle: later styles override earlier ones&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Inheritance
&lt;ul>
&lt;li>Children inherit from parents&lt;/li>
&lt;li>However, some special tags do not inherit styles&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Specificity
&lt;ul>
&lt;li>ID &amp;gt; class [attribute] &amp;gt; tag, specificity decreases&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="example-4">
&lt;a class="heading-anchor-link" href="#example-4">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">red&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">red&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">blue&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">blue&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">black&lt;/span> &lt;span class="cp">!important&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;blue red&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>hello world&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The actual text will be black because !important has the highest priority, but if you remove it, it will be blue because CSS cascading rules state that later styles override earlier ones.&lt;/p>
&lt;h2 id="frontend-performance-optimization">
&lt;a class="heading-anchor-link" href="#frontend-performance-optimization">Frontend Performance Optimization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-performance-optimization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>gzip compression / pre-compression&lt;/li>
&lt;li>HTML, CSS, JS minification&lt;/li>
&lt;li>Frontend caching&lt;/li>
&lt;li>Lazy loading, preloading, reducing JS bundle size&lt;/li>
&lt;li>Fully utilize request bandwidth, e.g., concurrent requests &lt;code>Note: single domain request count limit&lt;/code>&lt;/li>
&lt;li>CDN for static resources to improve cache/request speed&lt;/li>
&lt;li>Code optimization, e.g., SPA render optimization, reducing render frequency, using WebSocket instead of polling for some cases to reduce request count&lt;/li>
&lt;li>SSR (Server-Side Rendering)&lt;/li>
&lt;li>High-frequency high-consumption calculations can use Web Workers&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>These questions show that they are still basic problems, but we also need to understand that there are many frontend knowledge points today. In addition to being familiar with the frontend trinity [HTML, CSS, JS], we also need to be familiar with networking, algorithms, browser operating principles, etc.&lt;/p>
&lt;h2 id="recommended-resources">
&lt;a class="heading-anchor-link" href="#recommended-resources">Recommended Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://blog.bitsrc.io/hoisting-in-modern-javascript-let-const-and-var-b290405adfda" target="_blank" rel="noopener">https://blog.bitsrc.io/hoisting-in-modern-javascript-let-const-and-var-b290405adfda&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://limeii.github.io/2019/05/js-lexical-environment/" target="_blank" rel="noopener">JS: Deep Understanding of JavaScript - Lexical Environment&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>What Are Git Snapshots</title><link>https://en.1991421.cn/2020/12/20/what-is-git-snapshot/</link><pubDate>Sun, 20 Dec 2020 15:22:32 +0800</pubDate><guid>https://en.1991421.cn/2020/12/20/what-is-git-snapshot/</guid><description>&lt;blockquote>
&lt;p>The more I use Git, the more I like it. But some underlying knowledge isn&amp;rsquo;t systematic, such as what snapshots are and how Git stores them. I&amp;rsquo;m documenting it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="snapshot-recording">
&lt;a class="heading-anchor-link" href="#snapshot-recording">Snapshot recording&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="snapshot-recording"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>On each commit, Git scans all files in the repository. If a file changes, it generates a new &lt;a href="https://en.wikipedia.org/wiki/Binary_large_object" target="_blank" rel="noopener">Blob&lt;/a> that contains the full content at the time of the commit. If a file doesn&amp;rsquo;t change, it records a link to the previously stored file.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Each commit has its own index, which is used to locate both changed and unchanged files.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>The diagram below helps visualize the repository state for each snapshot (version).&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-20-153213.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="snapshot-storage">
&lt;a class="heading-anchor-link" href="#snapshot-storage">Snapshot storage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="snapshot-storage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Knowing how snapshots are recorded, where are they stored? In the hidden .git folder.&lt;/p>
&lt;p>There are many items in that folder. The ones we care about for snapshot storage are &lt;code>index&lt;/code> and &lt;code>objects&lt;/code>. For other parts, see Pro Git.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-20-154131.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>To understand how Git stores data, let&amp;rsquo;s initialize an empty project:&lt;/p>
&lt;p>&lt;code>mkdir git-demo &amp;amp; cd git-demo &amp;amp; git init &amp;amp; echo &amp;quot;just a demo&amp;quot;&amp;gt; README.md&lt;/code>&lt;/p>
&lt;p>Start executing Git operations.&lt;/p>
&lt;h3 id="git-add">
&lt;a class="heading-anchor-link" href="#git-add">git add&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="git-add"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When you run &lt;code>git add .&lt;/code>, the index file stores the index of files to be committed. To view it, use the low-level command &lt;code>git ls-files -s&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git ls-files -s
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="m">100644&lt;/span> a730a28e53d8defdda8fe953829afdfc906e463a 0 README.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: Because it is a binary file, you can&amp;rsquo;t view it as plain text, only in the format above. You can see the index records &lt;code>README.md&lt;/code> and the blob file name &lt;code>a730a28e53d8defdda8fe953829afdfc906e463a&lt;/code>, a 40-character SHA-1.&lt;/p>
&lt;p>The blob files are stored in &lt;code>.git/objects&lt;/code>. The first two characters &lt;code>a7&lt;/code> are the folder, and the remaining 38 characters are the file name.&lt;/p>
&lt;p>At this point, you can run &lt;code>git cat-file -p a730a2&lt;/code> to view the full content of the committed file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git cat-file -p a730a2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">just a demo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="git-commit">
&lt;a class="heading-anchor-link" href="#git-commit">git commit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="git-commit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When you run &lt;code>git commit -m 'init readme'&lt;/code>, after the commit succeeds:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s1">&amp;#39;init readme&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>master &lt;span class="o">(&lt;/span>root-commit&lt;span class="o">)&lt;/span> 79821c6&lt;span class="o">]&lt;/span> init readme
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">1&lt;/span> file changed, &lt;span class="m">1&lt;/span> insertion&lt;span class="o">(&lt;/span>+&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> create mode &lt;span class="m">100644&lt;/span> README.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Check the &lt;code>.git/objects&lt;/code> directory again and you will see two additional folders.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ ll .git/objects/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">total &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">3&lt;/span> qhe staff 96B Dec &lt;span class="m">20&lt;/span> 22:22 4e
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">3&lt;/span> qhe staff 96B Dec &lt;span class="m">20&lt;/span> 22:22 &lt;span class="m">79&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">3&lt;/span> qhe staff 96B Dec &lt;span class="m">20&lt;/span> 16:24 a7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">2&lt;/span> qhe staff 64B Dec &lt;span class="m">20&lt;/span> 16:24 info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">drwxr-xr-x &lt;span class="m">2&lt;/span> qhe staff 64B Dec &lt;span class="m">20&lt;/span> 16:24 pack
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, &lt;code>79&lt;/code> stores the content of the commit, while &lt;code>4e&lt;/code> stores a tree object that keeps file names and other info for that commit.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git cat-file -t 4edb6d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tree
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>At this point, we have a rough understanding of how Git records and stores snapshots during daily operations.&lt;/p>
&lt;h3 id="memory-usage">
&lt;a class="heading-anchor-link" href="#memory-usage">Memory usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="memory-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>As mentioned above, changed files are stored in full as binary blobs. Over time, this would consume a lot of space. Git optimizes for this.&lt;/p>
&lt;blockquote>
&lt;p>Git balances time and space by storing full content for the latest version, and only diffs for older or less frequently used versions. This balances storage and read/load speed.&lt;/p>
&lt;/blockquote>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A simplified diagram of daily Git operations:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-20-154508.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>During &lt;code>git add&lt;/code>, files are stored in the staging area index/objects.&lt;/li>
&lt;li>During &lt;code>git commit&lt;/code>, files are stored in the local repository objects.&lt;/li>
&lt;/ul>
&lt;p>Of course, during &lt;code>git push&lt;/code>, these objects are sent upstream. But remember Git is distributed, so the upstream and local contents are actually the same.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Git feels simple yet very powerful, which is a hallmark of excellent software design.&lt;/li>
&lt;li>Understanding Git internals helps you use Git more efficiently and gives perspective for daily development issues, such as the storage strategy above.&lt;/li>
&lt;/ul>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/27264809/where-git-staged-files-are-stored" target="_blank" rel="noopener">Where Git staged files are stored?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://titangene.github.io/article/git-index.html" target="_blank" rel="noopener">Deep dive into Git: index file&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Encoding Pitfalls to Watch</title><link>https://en.1991421.cn/2020/12/16/encoding-issues/</link><pubDate>Wed, 16 Dec 2020 09:55:53 +0800</pubDate><guid>https://en.1991421.cn/2020/12/16/encoding-issues/</guid><description>&lt;blockquote>
&lt;p>Recently I hit two frontend errors: &lt;code>SyntaxError: The URL must not contain a fragment&lt;/code> and &lt;code>window.btoa&lt;/code> throwing on input. Both stemmed from encoding gaps—easy to forget when frameworks usually handle them. Notes below for future reference.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error-screenshots">
&lt;a class="heading-anchor-link" href="#error-screenshots">Error screenshots&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-screenshots"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-17-151538.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-17-151645.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="background-refresher">
&lt;a class="heading-anchor-link" href="#background-refresher">Background refresher&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background-refresher"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="uri-vs-url">
&lt;a class="heading-anchor-link" href="#uri-vs-url">URI vs. URL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="uri-vs-url"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>URI = Uniform Resource Identifier&lt;/p>
&lt;p>URL = Uniform Resource Locator&lt;/p>
&lt;p>Every URL is a URI, but not every URI is a URL. Example of a URI that isn’t a URL: &lt;code>mailto:alan@1991421.cn&lt;/code>.&lt;/p>
&lt;h3 id="encodeuri-vs-encodeuricomponent">
&lt;a class="heading-anchor-link" href="#encodeuri-vs-encodeuricomponent">&lt;code>encodeURI&lt;/code> vs. &lt;code>encodeURIComponent&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="encodeuri-vs-encodeuricomponent"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>JavaScript offers these helpers because certain characters in a URI have special meaning (&lt;code>: ; / ? : @ &amp;amp; = + $ , #&lt;/code>, etc.). &lt;code>encodeURI&lt;/code> leaves the separators intact; &lt;code>encodeURIComponent&lt;/code> escapes everything, making it safer for query parameters.&lt;/p>
&lt;h3 id="windowbtoa">
&lt;a class="heading-anchor-link" href="#windowbtoa">&lt;code>window.btoa&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="windowbtoa"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>See the MDN docs or my &lt;a href="https://1991421.cn/2020/07/02/7abb323a/" target="_blank" rel="noopener">earlier post&lt;/a>. &lt;code>btoa&lt;/code> encodes binary strings to ASCII. Feed it characters outside Latin-1 (e.g., Chinese or Japanese) and it will throw unless you encode them first.&lt;/p>
&lt;h2 id="fixes">
&lt;a class="heading-anchor-link" href="#fixes">Fixes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fixes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>When building URLs that include non-ASCII characters, encode the relevant parts before passing them to the URL constructor.&lt;/li>
&lt;li>Before calling &lt;code>window.btoa&lt;/code>, normalize the string (e.g., via &lt;code>encodeURIComponent&lt;/code>) so it contains only ASCII.&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Libraries like Axios hide these details, which is convenient but easy to misremember. Understanding the underlying rules makes debugging far less painful.&lt;/p></description></item><item><title>Web Performance: Reduce Re-Renders</title><link>https://en.1991421.cn/2020/12/08/web-perf-reduce-renders/</link><pubDate>Tue, 08 Dec 2020 14:47:21 +0800</pubDate><guid>https://en.1991421.cn/2020/12/08/web-perf-reduce-renders/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-08-222757.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>SPAs aim to deliver a smooth, app-like experience. As features grow, code can bloat and performance bottlenecks appear—time for optimization. One lever is reducing render frequency.&lt;/p>
&lt;/blockquote>
&lt;p>Using React as the example (Angular/Vue are similar):&lt;/p>
&lt;p>All frameworks re-render DOM from component state. Components partition the UI, so updates are localized. Misuse can cause duplicate/unnecessary renders. The goal: render only what needs rendering, when it needs rendering.&lt;/p>
&lt;h2 id="react-strategies">
&lt;a class="heading-anchor-link" href="#react-strategies">React Strategies&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-strategies"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React class components offer &lt;code>shouldComponentUpdate&lt;/code>. By default, changes in props/state trigger re-render. Also, when a parent renders, children render too—even if their props didn’t change.&lt;/p>
&lt;p>Since React 15.3+, &lt;code>PureComponent&lt;/code> implements &lt;code>shouldComponentUpdate&lt;/code> with shallow comparison; if nothing changed, it skips render. For function components (React 16.6+), &lt;code>React.memo&lt;/code> provides the same benefit.&lt;/p>
&lt;p>Can we just switch everything to &lt;code>PureComponent&lt;/code>/&lt;code>memo&lt;/code>? No.&lt;/p>
&lt;p>Shallow compares work best with primitives. With object references, equal-by-value changes may still trigger renders. Choose the right approach per prop type. Ultimately, control render frequency with &lt;code>shouldComponentUpdate&lt;/code> or custom comparators passed to &lt;code>memo&lt;/code>.&lt;/p>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In a recent optimization, the footer rendered 7 times. It depended on many Redux objects. &lt;code>PureComponent&lt;/code> didn’t help because props were complex objects. Business-specific checks on actual values reduced renders to 2—and correct. From 7 to 2 is a big win.&lt;/p>
&lt;p>For complex interactive pages, the outer components suffer most: redundant renders cascade into nested trees—costly.&lt;/p>
&lt;h2 id="redux-pitfalls">
&lt;a class="heading-anchor-link" href="#redux-pitfalls">Redux Pitfalls&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-pitfalls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In many React stacks, components connect to Redux. A common mistake is connecting overly large objects (e.g., &lt;code>person&lt;/code>) when the component only needs &lt;code>person.name&lt;/code>. Any change in &lt;code>person&lt;/code> triggers renders even if the used field didn’t change. Connect at the right granularity and level.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>DOM operations are expensive. Reducing frequency is a key frontend goal.&lt;/p></description></item><item><title>JIT vs AOT</title><link>https://en.1991421.cn/2020/12/06/jit-vs-aot/</link><pubDate>Sun, 06 Dec 2020 20:43:39 +0800</pubDate><guid>https://en.1991421.cn/2020/12/06/jit-vs-aot/</guid><description>&lt;blockquote>
&lt;p>When working with Angular, you encounter these two compilation methods AOT and JIT, but when working with React they&amp;rsquo;re not mentioned. Is this unique to NG? Of course not, Java also mentions JIT. Here I&amp;rsquo;ll discuss the differences between the two.&lt;/p>
&lt;/blockquote>
&lt;p>If there are any errors, welcome corrections.&lt;/p>
&lt;h2 id="definitions">
&lt;a class="heading-anchor-link" href="#definitions">Definitions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="definitions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here taking an explanation from &lt;a href="https://angular.io/guide/aot-compiler" target="_blank" rel="noopener">Angular&lt;/a>:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>JIT：just-in-time&lt;/p>
&lt;p>Just-in-Time (JIT), which compiles your app in the browser at runtime.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-06-210909.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>AOT: ahead of time&lt;/p>
&lt;p>Ahead-of-Time (AOT), which compiles your app and libraries at build time.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-06-210919.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="example-analysis">
&lt;a class="heading-anchor-link" href="#example-analysis">Example Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using an NG demo as an example to illustrate:&lt;/p>
&lt;p>JIT compiled code is different from AOT compiled code. JIT compiled code is not JS that browser engines can execute normally. As mentioned in the definition, JIT requires runtime compilation. For example, Angular syntax is not understood by browsers, so a built-in compiler is needed. Therefore, JIT compiled code is generally larger, largely due to the built-in compiler.&lt;/p>
&lt;p>For example, with an NG directive like routerLink, in JIT compiled code we can still see code blocks like this:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-06-211217.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>But in AOT compiled code, such code doesn&amp;rsquo;t exist.&lt;/p>
&lt;h2 id="react-java">
&lt;a class="heading-anchor-link" href="#react-java">React, Java&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-java"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React not mentioning JIT and AOT doesn&amp;rsquo;t mean there&amp;rsquo;s no need for this. For high-level language compilation, both modes exist as choices. React doesn&amp;rsquo;t mention it because React itself doesn&amp;rsquo;t provide both methods simultaneously. Compilation and packaging will compile JSX to JS, so it can be said React only has AOT compilation.&lt;/p>
&lt;p>Java is actually JIT compiled. When we package a Spring WEB application, it generates a jar or war package. After decompression, you&amp;rsquo;ll see a bunch of class files, but these files are not machine code - they are Java bytecode, an intermediate form between Java files and machine code. In theory, JIT compilation should be slow, but in actual operation, aside from initial slow loading, it doesn&amp;rsquo;t feel slow later. This is because JVM has algorithms that compile frequently executed bytecode into machine code, ensuring faster execution response.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-06-210824.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/46777779/is-there-a-aot-for-reactjs-like-the-angular-4-0-has" target="_blank" rel="noopener">https://stackoverflow.com/questions/46777779/is-there-a-aot-for-reactjs-like-the-angular-4-0-has&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://angular.io/guide/aot-compiler" target="_blank" rel="noopener">https://angular.io/guide/aot-compiler&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Limitations of React's componentWillUnmount</title><link>https://en.1991421.cn/2020/12/05/react-componentwillunmount/</link><pubDate>Sat, 05 Dec 2020 16:30:03 +0800</pubDate><guid>https://en.1991421.cn/2020/12/05/react-componentwillunmount/</guid><description>&lt;blockquote>
&lt;p>componentWillUnmount is triggered when a component is about to be destroyed, but if the user directly closes the browser tab, this hook will not be executed.&lt;/p>
&lt;/blockquote>
&lt;p>This issue needs to be considered in specific scenarios. For example, when I was working on WebSocket-related development, the scenario was: when a user enters a details page, they subscribe to an article topic, and when the user navigates to other URLs, the article subscription is canceled. Therefore, I placed the unsubscribe action in componentWillUnmount. However, if the user directly closes the TAB page, the program cannot detect this, leading to bugs.&lt;/p>
&lt;h2 id="why">
&lt;a class="heading-anchor-link" href="#why">Why?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Directly closing a tab means immediately destroying the HTML, JS, and CSS resources of that tab page, so React&amp;rsquo;s lifecycle methods won&amp;rsquo;t be executed. Therefore, in actual development, it&amp;rsquo;s important to be aware of this limitation of the hook.&lt;/p>
&lt;h2 id="native-window-beforeunload-event">
&lt;a class="heading-anchor-link" href="#native-window-beforeunload-event">Native window beforeunload Event&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="native-window-beforeunload-event"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above issue can be complementarily solved using the native window beforeunload event. Example below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">super&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">removeEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;beforeunload&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;beforeunload&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">listener&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">listener&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;will unmount&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">returnValue&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>This approach allows executing specified logic even when users directly close the web page.&lt;/li>
&lt;li>While this solution works, it&amp;rsquo;s important to note that this event should be placed in root components or similar locations. The reason is simple: if it&amp;rsquo;s bound within looped list components, the overhead would be significant, so this needs attention.&lt;/li>
&lt;li>Although this discussion focuses on React&amp;rsquo;s componentWillUnmount, similar issues exist with other JavaScript libraries like Angular and Vue.&lt;/li>
&lt;/ul></description></item><item><title>TypeScript Enum vs. ES6 Symbol</title><link>https://en.1991421.cn/2020/12/02/typescriptenumes6symbol/</link><pubDate>Wed, 02 Dec 2020 09:44:38 +0800</pubDate><guid>https://en.1991421.cn/2020/12/02/typescriptenumes6symbol/</guid><description>&lt;blockquote>
&lt;p>JavaScript&amp;rsquo;s primitive data types added the Symbol type when ES6 arrived, and ES6 (ES2015) was released 5.5 years ago.&lt;/p>
&lt;p>However, in actual development, I haven&amp;rsquo;t used Symbol much because in the TypeScript environment, I seem to consider it even less since TypeScript has enum types. But after serious study and research, I realize this understanding was wrong.&lt;/p>
&lt;p>Here I&amp;rsquo;ll mark down the differences between the two to clarify their distinctions and meanings for healthier and more reasonable usage.&lt;/p>
&lt;/blockquote>
&lt;h2 id="typescript-enum">
&lt;a class="heading-anchor-link" href="#typescript-enum">TypeScript Enum&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript-enum"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, TypeScript is just an intermediate language, a development language, and TypeScript ultimately needs to be compiled to JavaScript, so this type itself is just a wrapper.&lt;/p>
&lt;p>For example, if we define an enum like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">enum&lt;/span> &lt;span class="nx">Config&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">CSV&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">JSON&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After TSC compilation, we can see it becomes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">Config&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">Config&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;CSV&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;CSV&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">Config&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;JSON&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;JSON&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})(&lt;/span>&lt;span class="nx">Config&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">Config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Looking at TypeScript&amp;rsquo;s official description of Enum:&lt;/p>
&lt;blockquote>
&lt;p>Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.&lt;/p>
&lt;/blockquote>
&lt;p>Takeaways:&lt;/p>
&lt;ul>
&lt;li>Enums define a set of named constants.&lt;/li>
&lt;li>They compile to JavaScript objects; no runtime magic involved.&lt;/li>
&lt;li>Enum values can be numbers or strings, but not other types.&lt;/li>
&lt;/ul>
&lt;h2 id="es6-symbol">
&lt;a class="heading-anchor-link" href="#es6-symbol">ES6 Symbol&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="es6-symbol"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Symbol is a new data type added in ES6, designed to represent unique identifiers for object properties and prevent property conflicts.&lt;/p>
&lt;p>Here&amp;rsquo;s the official description from MDN:&lt;/p>
&lt;blockquote>
&lt;p>The data type symbol is a primitive data type. The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class, but is incomplete as a constructor because it does not support the syntax &amp;ldquo;new Symbol()&amp;rdquo;.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Every symbol value returned from Symbol() is unique. A symbol value may be used as an identifier for object properties; this is the data type&amp;rsquo;s primary purpose, although other use-cases exist, such as enabling opaque data types, or serving as an implementation-supported unique identifier in general. Some further explanation about purpose and usage can be found in the glossary entry for Symbol.&lt;/p>
&lt;/blockquote>
&lt;p>Takeaways:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Symbol('hey') === Symbol('hey')&lt;/code> is &lt;code>false&lt;/code>; each call produces a unique value.&lt;/li>
&lt;li>Symbols can emulate “private” properties because JSON serialization and &lt;code>Object.keys&lt;/code> skip them.&lt;/li>
&lt;/ul>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Choose the construct that fits: use symbols when you just need unique identifiers on the frontend, and enums when values must serialize cleanly or remain human-readable.&lt;/p></description></item><item><title>rsync total size is 0 speedup is 0.00</title><link>https://en.1991421.cn/2020/12/01/rsync-total-size-is-0-speedup-is-0-00/</link><pubDate>Tue, 01 Dec 2020 11:54:36 +0800</pubDate><guid>https://en.1991421.cn/2020/12/01/rsync-total-size-is-0-speedup-is-0-00/</guid><description>&lt;blockquote>
&lt;p>While wiring GitHub Actions to deploy a static site to my VPS, the sync finished with files present but empty. rsync reported “total size is 0 speedup is 0.00”. Time to investigate.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-happened">
&lt;a class="heading-anchor-link" href="#what-happened">What Happened&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-happened"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Command used:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">rsync -zvr -e ssh public root@1991421.cn:/var/www/blog
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-01-120002.jpeg"
alt="https://static.1991421.cn/2020/2020-12-01-120002.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;code>rsync total size is 0 speedup is 0.00&lt;/code>&lt;/p>
&lt;p>Logging into the VPS confirmed every file was empty:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-01-120023.jpeg"
alt="https://static.1991421.cn/2020/2020-12-01-120023.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Tried SCP:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">scp -r public root@1991421.cn:/var/www/blog/public
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Same result: empty files.&lt;/p>
&lt;h2 id="troubleshooting">
&lt;a class="heading-anchor-link" href="#troubleshooting">Troubleshooting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="troubleshooting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Both rsync and scp failed → not a command syntax issue.&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Wrong SSH key would show a different error.&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Forcing the runner to macOS didn’t help.&lt;/li>
&lt;/ul>
&lt;p>So the difference had to be between local and CI environments. Google didn’t turn up much on “total size is 0 speedup is 0.00”, so I asked the action’s author. The answer was blunt: if speed is zero, the files already contain zero bytes. 🤯&lt;/p>
&lt;p>That hint led me to re-check the build process. Locally I ran Node v10; on GitHub Actions it was Node 14.x. Hexo (the static generator) depends on Node, so a version mismatch could easily break the output.&lt;/p>
&lt;p>After aligning Node versions, deployment worked:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-12-01-120106.jpeg"
alt="https://static.1991421.cn/2020/2020-12-01-120106.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="lessons-learned">
&lt;a class="heading-anchor-link" href="#lessons-learned">Lessons Learned&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lessons-learned"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Hexo is built on Node.js; keep versions consistent across environments.&lt;/li>
&lt;li>When rsync reports zero size and zero speed, suspect empty source files.&lt;/li>
&lt;li>GitHub Actions turned out faster than Travis for this workflow—another bonus once the bug was fixed.&lt;/li>
&lt;/ul></description></item><item><title>JavaScript Regular Expressions: Literal vs Constructor</title><link>https://en.1991421.cn/2020/11/28/javascript-vs/</link><pubDate>Sat, 28 Nov 2020 18:20:48 +0800</pubDate><guid>https://en.1991421.cn/2020/11/28/javascript-vs/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;ve never systematically summarized the differences between literals and constructors when declaring regular expressions, so I&amp;rsquo;m taking some time to do that now.&lt;/p>
&lt;/blockquote>
&lt;h2 id="differences">
&lt;a class="heading-anchor-link" href="#differences">Differences&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="differences"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>In literals, special characters are parsed directly, while in constructors you need to add backslashes for escaping. For example, \d is treated as the string \d, but&lt;/li>
&lt;li>The forward slashes around literals are just delimiters to indicate it&amp;rsquo;s a regular expression. If you write them in a constructor, they mean matching actual slashes&lt;/li>
&lt;li>Literal expressions have better readability than constructors&lt;/li>
&lt;/ul>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The following regular expression matches are equivalent:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;rt:60C1C036-42FA-4073-B10B-1969BD2358FB@00000000077&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nb">RegExp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;rt:([^@]+)@(\\d+)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">exec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/rt:([^@]+)@(\d+)/&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="how-to-choose">
&lt;a class="heading-anchor-link" href="#how-to-choose">How to Choose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-choose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the regular expression pattern is fixed, prefer using literals. But if the pattern is a variable that needs to be determined from context, then you should use the constructor.&lt;/p>
&lt;h2 id="extension">
&lt;a class="heading-anchor-link" href="#extension">Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="literals-vs-variables">
&lt;a class="heading-anchor-link" href="#literals-vs-variables">Literals vs Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="literals-vs-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Literals are different from variables. For example, in &lt;code>a=1&lt;/code>, a is a variable, but we can also say that 1 is a literal because we can directly see what the value is from its appearance. So literals refer to values, while variables refer to parameters - one on the left side, one on the right side.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Text processing inevitably involves regular expressions, so it&amp;rsquo;s important to pay attention and summarize these concepts.&lt;/p></description></item><item><title>How to Set Up a Network Proxy in Terminal Shell</title><link>https://en.1991421.cn/2020/11/17/terminal-proxy-config/</link><pubDate>Tue, 17 Nov 2020 19:27:14 +0800</pubDate><guid>https://en.1991421.cn/2020/11/17/terminal-proxy-config/</guid><description>&lt;blockquote>
&lt;p>Terminal Shell on OS does not use the system proxy, so even if the system proxy is enabled, commands like curl or npm in iTerm2 may still face network issues.&lt;/p>
&lt;p>Proxy apps like Surge or others that support modifying the network card can force the terminal to use the proxy, but sometimes, we don&amp;rsquo;t want to use this method. So, Can the terminal shell use the proxy?&lt;/p>
&lt;p>Yes, there is. Here is the solution.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2024/2024-07-13-122755.jpeg"
alt="https://static.1991421.cn/2024/2024-07-13-122755.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="shell-configuration-file">
&lt;a class="heading-anchor-link" href="#shell-configuration-file">Shell Configuration File&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="shell-configuration-file"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here, take bash as an example. Open the bash_profile by vi/.bash_profile&lt;code>. If you usually use zsh, you can also &lt;/code>vi ~/.zshrc`.&lt;/p>
&lt;p>Add the following configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">http_proxy&lt;/span>&lt;span class="o">=&lt;/span>socks5://127.0.0.1:6153
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">https_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$http_proxy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">all_proxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$http_proxy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">disproxy&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;unset http_proxy https_proxy all_proxy&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you only want to solve the terminal HTTP request proxy, you can skip configuring all_proxy. Since requests are not always HTTP, you can configure all_proxy to solve HTTP requests.&lt;/p>
&lt;p>&lt;strong>Note that git ssh will not use this configuration. The solution is to configure proxyCommand for git separately or use the enhanced mode of the proxy software.&lt;/strong>&lt;/p>
&lt;h2 id="apply-configuration">
&lt;a class="heading-anchor-link" href="#apply-configuration">Apply Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="apply-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>source ~/.bash_profile&lt;/code>&lt;/p>
&lt;h2 id="test">
&lt;a class="heading-anchor-link" href="#test">Test&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="test"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>curl -i https://google.com&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-11-17-190707.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If the system proxy service itself has network request packet capture, you can judge whether the proxy is effective through packet capture.&lt;/p>
&lt;h2 id="temporarily-disable-proxy">
&lt;a class="heading-anchor-link" href="#temporarily-disable-proxy">Temporarily Disable Proxy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="temporarily-disable-proxy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Because we have added disproxy in the configuration, execute &lt;code>disproxy&lt;/code>.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This way, the requests sent by the terminal shell can use the proxy service.&lt;/p></description></item><item><title>TypeScript Explained (Simple Guide)</title><link>https://en.1991421.cn/2020/11/10/typescript/</link><pubDate>Tue, 10 Nov 2020 15:28:34 +0800</pubDate><guid>https://en.1991421.cn/2020/11/10/typescript/</guid><description>&lt;blockquote>
&lt;p>A teammate recently ran into an issue and assumed it was caused by the TypeScript compiler. They even sent me this link: &lt;a href="https://github.com/kulshekhar/ts-jest/issues/1822" target="_blank" rel="noopener">flatMap is not a function #1822&lt;/a>.&lt;/p>
&lt;p>The fix was straightforward—the error clearly comes from calling array methods on a non-array—but the teammate still blamed TypeScript. That exposes a fundamental misunderstanding. To avoid repeating the same explanations, I&amp;rsquo;m summarizing the basics here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-11-10-235902.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="positioning-typescript">
&lt;a class="heading-anchor-link" href="#positioning-typescript">Positioning TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="positioning-typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Typed JavaScript at Any Scale.&lt;/p>
&lt;/blockquote>
&lt;p>That&amp;rsquo;s the official tagline. TypeScript is simply JavaScript with types—&lt;code>Type + Script&lt;/code>. The language itself doesn&amp;rsquo;t take responsibility for JavaScript runtime compatibility.&lt;/p>
&lt;h2 id="typescripts-core-weapon-types">
&lt;a class="heading-anchor-link" href="#typescripts-core-weapon-types">TypeScript&amp;rsquo;s Core Weapon: Types&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescripts-core-weapon-types"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>JavaScript is flexible, but that freedom has a cost: higher risk and lower maintainability. TypeScript brings Java-like structure so every object and function has a clear type. It&amp;rsquo;s more flexible than Java though—union types, utility types, and so on.&lt;/p>
&lt;p>Notice that TypeScript includes both &lt;code>any&lt;/code> and &lt;code>unknown&lt;/code>, representing the upper and lower bounds of types. But remember: &lt;em>if your entire project relies on &lt;code>any&lt;/code>, &lt;code>unknown&lt;/code>, and &lt;code>@ts-ignore&lt;/code>, you might as well skip TypeScript.&lt;/em> Unlimited and unknown types give you no safety net.&lt;/p>
&lt;h2 id="using-typescript-in-real-projects">
&lt;a class="heading-anchor-link" href="#using-typescript-in-real-projects">Using TypeScript in Real Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-typescript-in-real-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In practice, TypeScript is your development language. Lean on types so you catch bugs during development—not after the compiler strips types, the bundler runs, and production crashes.&lt;/p>
&lt;h2 id="recommended-tooling">
&lt;a class="heading-anchor-link" href="#recommended-tooling">Recommended Tooling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-tooling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Babel – transpile TypeScript during development or bundling.
&lt;ul>
&lt;li>Tools like awesome-typescript-loader are outdated. Babel is more powerful, and its plugin ecosystem helps with JavaScript compatibility.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>ESLint – enforce code quality for both TS and JS.
&lt;ul>
&lt;li>TSLint is deprecated; ESLint now covers everything.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="choosing-a-typescript-version">
&lt;a class="heading-anchor-link" href="#choosing-a-typescript-version">Choosing a TypeScript Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="choosing-a-typescript-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>In general, the newest version is best. TypeScript is just a typed superset of JavaScript, so upgrading won&amp;rsquo;t break runtime behavior. In fact, newer versions improve type safety.&lt;/li>
&lt;li>TypeScript ultimately compiles to JavaScript, which runs in browsers or Node. Compatibility issues come from the compilation settings and target runtime.&lt;/li>
&lt;li>In real projects, dependencies often ship TypeScript code or definitions, which can make upgrades harder. To manage this:
&lt;ul>
&lt;li>Keep your version close to the versions used by your dependencies—minor version differences are usually fine.&lt;/li>
&lt;li>Some teams disable type checking during bundling. I don&amp;rsquo;t recommend it, but it&amp;rsquo;s technically possible since types don&amp;rsquo;t affect runtime behavior.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In short, TypeScript only adds types. If you adopt it, make the most of those types and be explicit wherever you can.&lt;/p></description></item><item><title>Best Practices for Batch Updating Production Data</title><link>https://en.1991421.cn/2020/11/03/batch-update-production-data/</link><pubDate>Tue, 03 Nov 2020 16:09:31 +0800</pubDate><guid>https://en.1991421.cn/2020/11/03/batch-update-production-data/</guid><description>&lt;blockquote>
&lt;p>Lately, I&amp;rsquo;ve been spending a lot of time working with SQL and have encountered a few pitfalls. Here are some insights on how to handle large-scale data updates in production.&lt;/p>
&lt;/blockquote>
&lt;h2 id="background">
&lt;a class="heading-anchor-link" href="#background">Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In our current system, performing complex &lt;code>UPDATE&lt;/code> operations on tables with hundreds of thousands of rows can lead to performance issues or even system crashes. To mitigate this, we adopted a strategy of breaking the updates into small, manageable chunks.&lt;/p>
&lt;h2 id="scripted-approach">
&lt;a class="heading-anchor-link" href="#scripted-approach">Scripted Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scripted-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The following Node.js script demonstrates how we generate batched SQL statements from a list of primary keys.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @description
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 1. Query the primary keys of the target data and export them to a CSV file.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 2. Define a single-transaction UPDATE SQL template.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 3. Run this script to generate the batched SQL file.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 4. Execute the resulting SQL file on the target server.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Note: Adjust CHUNK_SIZE to control the number of records updated per transaction.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">csv&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;csv-parser&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">fs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;fs&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">ids&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">CHUNK_SIZE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Number of records per batch
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Input file containing IDs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">INPUT_FILE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;quote_ids.csv&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Target output SQL file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">OUTPUT_FILE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;repair_data.sql&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">createSQLTemplate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">idArr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">UPDATE `&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="sb">`.quote a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">LEFT JOIN `&lt;/span>&lt;span class="nx">b&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">prod&lt;/span>&lt;span class="sb">`.geographic_unit b
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> ON a.sales_org = b.sales_org
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> AND a.sales_office = b.sales_office
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> AND a.business_unit = b.business_unit
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> AND a.country = b.country_iso_code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">SET a.geographic_unit_id = b.id
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">WHERE a.id IN (&amp;#39;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">idArr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">&amp;#39;);\n\n\n`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createReadStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">INPUT_FILE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">csv&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">row&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ids&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;end&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">chunkIds&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ids&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reduce&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">group&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">floor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">CHUNK_SIZE&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">group&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">OUTPUT_FILE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">chunkIds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">createSQLTemplate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SQL generation complete.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Using this method prevents long-running transactions that could lock tables or exhaust database resources.&lt;/p>
&lt;h2 id="reflections">
&lt;a class="heading-anchor-link" href="#reflections">Reflections&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reflections"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Data Volume vs. Performance&lt;/strong>: Even with only 200,000 rows, queries can become sluggish. This is often due to large fields (like &lt;code>TEXT&lt;/code>) being included in the result set unnecessarily. Always select only the columns you need; this is why using &lt;code>SELECT *&lt;/code> is discouraged. Complexity also increases significantly when joining multiple large tables.&lt;/li>
&lt;li>&lt;strong>Strict Filtering&lt;/strong>: For any &lt;code>UPDATE&lt;/code> operation, a &lt;code>WHERE&lt;/code> clause is mandatory. You must be extremely certain of your logic before performing an update without a precise filter.&lt;/li>
&lt;li>&lt;strong>Root Cause Analysis&lt;/strong>: If you find yourself needing to perform massive data corrections in production, it usually signals a bug in the application logic. Furthermore, if the update process is overly complicated, it might indicate that the database schema itself needs a redesign.&lt;/li>
&lt;/ul></description></item><item><title>Daily Challenge — Predict the Rendered Value</title><link>https://en.1991421.cn/2020/10/31/daily-quiz-render-value/</link><pubDate>Sat, 31 Oct 2020 23:59:37 +0800</pubDate><guid>https://en.1991421.cn/2020/10/31/daily-quiz-render-value/</guid><description>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-vue" data-lang="vue">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">template&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;{{&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">b&lt;/span> &lt;span class="p">}}&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">template&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">a&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">created&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mounted&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>During instance initialization, Vue converts properties into getters/setters, so the property must exist on &lt;code>data&lt;/code> to become reactive. Because &lt;code>a.b&lt;/code> isn’t declared up front, it’s non-reactive and the view won’t update when it changes.&lt;/li>
&lt;li>Lifecycle-wise, &lt;code>created&lt;/code> runs before &lt;code>mounted&lt;/code>, so the displayed value is 1.&lt;/li>
&lt;li>The question probes two concepts: Vue’s reactivity system and the timing difference between &lt;code>created&lt;/code> and &lt;code>mounted&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Try it yourself in this sandbox: &lt;a href="https://codesandbox.io/s/meiriyiti-20201031-zjuhq?file=/src/components/HelloWorld.vue" target="_blank" rel="noopener">code link&lt;/a>&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Even though this example is in Vue, similar pitfalls show up in React and Angular with their own twists. For instance, React tracks state by reference; without &lt;code>setState&lt;/code>, the view won’t re-render.&lt;/p></description></item><item><title>Daily Challenge — Avoid Infinite Loop</title><link>https://en.1991421.cn/2020/10/29/daily-quiz-fix-infinite-loop/</link><pubDate>Thu, 29 Oct 2020 21:30:43 +0800</pubDate><guid>https://en.1991421.cn/2020/10/29/daily-quiz-fix-infinite-loop/</guid><description>&lt;p>Here’s a frontend quiz: “Modify the code so it doesn’t cause an infinite loop.”&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">random&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, keep in mind:&lt;/p>
&lt;ul>
&lt;li>JS is single-threaded&lt;/li>
&lt;li>There’s no true parallelism in JS, only concurrency&lt;/li>
&lt;/ul>
&lt;p>Therefore the loop above will run forever if executed on the main thread. To avoid blocking, we need asynchrony or multiple threads.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on the above, the fixes are straightforward.&lt;/p>
&lt;h3 id="web-worker">
&lt;a class="heading-anchor-link" href="#web-worker">Web Worker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web-worker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Web Workers run on separate threads, so they don’t block the main thread.&lt;/li>
&lt;li>As IE has been replaced by Edge, Worker support is solid across non-IE browsers — safe to use.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// main.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">worker&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Worker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;worker.js&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">worker&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onmessage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// handle the data received from the worker
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// worker.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">random&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mf">0.9&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">postMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="concurrentthreadjs-library">
&lt;a class="heading-anchor-link" href="#concurrentthreadjs-library">Concurrent.Thread.js Library&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concurrentthreadjs-library"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If you can’t use Web Workers, this library simulates “multithreading” via asynchronous execution.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Concurrent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Thread&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">random&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Library link — &lt;a href="https://github.com/penghuwan/concurrent-thread.js" target="_blank" rel="noopener">GitHub&lt;/a>&lt;/p></description></item><item><title>Parent-Child Commits in Git Log</title><link>https://en.1991421.cn/2020/10/29/git-logcommit/</link><pubDate>Thu, 29 Oct 2020 15:37:15 +0800</pubDate><guid>https://en.1991421.cn/2020/10/29/git-logcommit/</guid><description>&lt;blockquote>
&lt;p>Learning to read Git log is also an essential skill. Previously, I had some confusion about parent-child relationships in commits, so here&amp;rsquo;s a summary to organize my thoughts.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-29-154033.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>If there&amp;rsquo;s no MR, commits in a single branch are actually a straight line. But because of MR, the entire commit graph becomes a tree structure.&lt;/strong>&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>For a commit, the commit it was based on when created (i.e., the previous commit) will be its parent commit&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For a commit, if a commit is created based on it, that will be its child commit&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Each commit&amp;rsquo;s &lt;code>parent commit and child commit are not necessarily unique&lt;/code>&lt;/p>
&lt;p>As shown below, this merge commit will have two parents&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-29-171017.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-29-171200.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Git looks simple, but using it well requires skill. Personally, I think the best way to learn is to think about principles during practice and summarize more.&lt;/p></description></item><item><title>Upgrading TypeScript to v4</title><link>https://en.1991421.cn/2020/10/21/typescriptv4/</link><pubDate>Wed, 21 Oct 2020 23:43:49 +0800</pubDate><guid>https://en.1991421.cn/2020/10/21/typescriptv4/</guid><description>&lt;blockquote>
&lt;p>I upgraded our project to TypeScript 4.0 to use newer TS/JS features like logical assignment operators. Here are the details.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>Remember: TypeScript is about types—upgrading shouldn’t change runtime behavior.&lt;/em>&lt;/p>
&lt;h2 id="package-updates">
&lt;a class="heading-anchor-link" href="#package-updates">Package Updates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-updates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;typescript&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.0.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@typescript-eslint/eslint-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@typescript-eslint/eslint-plugin-tslint&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@typescript-eslint/parser&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.5.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Upgrade related @typescript-eslint packages because ESLint relies on the TS parser.&lt;/li>
&lt;li>Update other vendor types as needed when errors appear.&lt;/li>
&lt;/ul>
&lt;h2 id="issues-encountered">
&lt;a class="heading-anchor-link" href="#issues-encountered">Issues Encountered&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="issues-encountered"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;code>Parsing error: Cannot read property 'map' of undefined&lt;/code> — fixed by upgrading the @typescript-eslint packages above.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>Property 'canCostRelief' does not exist on type 'never'&lt;/code> — TypeScript narrowed an intersection to &lt;code>never&lt;/code> because &lt;code>selected&lt;/code> had conflicting types (&lt;code>boolean&lt;/code> vs &lt;code>string&lt;/code>). Align the types.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>This expression is not callable&lt;/code> — &lt;code>quoteLines&lt;/code> was typed as &lt;code>A | B&lt;/code>, but the helper expected &lt;code>A&lt;/code>. Narrow it (&lt;code>quoteLines as A[]&lt;/code>) or adjust the function signature.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Version upgrades can be bumpy, but they improve type safety and unlock language features. Treat issues as learning opportunities and don’t postpone upgrades indefinitely.&lt;/p></description></item><item><title>Reading the Meituan Homepage HTML Source</title><link>https://en.1991421.cn/2020/10/10/html/</link><pubDate>Sat, 10 Oct 2020 17:21:38 +0800</pubDate><guid>https://en.1991421.cn/2020/10/10/html/</guid><description>&lt;p>Homepage: &lt;a href="https://www.meituan.com" target="_blank" rel="noopener">https://www.meituan.com&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>Front-end code is transparent—we can always inspect it. There’s a lot to learn from viewing production source. Here’s what stood out to me on Meituan’s homepage.&lt;/p>
&lt;/blockquote>
&lt;h2 id="seo">
&lt;a class="heading-anchor-link" href="#seo">SEO&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="seo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>美团网-美食_团购_外卖_酒店_旅游_电影票_吃喝玩乐全都有&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;description&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;美团网:美食攻略,外卖网上订餐,酒店预订,旅游团购,飞机票火车票,电影票,ktv团购吃喝玩乐全都有!店铺信息查询,商家评分/评价一站式生活服务网站&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;keywords&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;美食,团购,外卖,网上订餐,酒店,旅游,电影票,火车票,飞机票&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Meta tags still matter for SEO. For SPA apps they’re trickier, which is why SSR exists.&lt;/p>
&lt;h2 id="format-detection">
&lt;a class="heading-anchor-link" href="#format-detection">Format detection&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="format-detection"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;format-detection&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;telephone=no&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;format-detection&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;address=no&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ul>
&lt;li>Disables automatic detection of phone numbers, addresses, etc., preventing accidental clicks.&lt;/li>
&lt;li>Non-standard tag; works in Safari only. See the &lt;a href="https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html#//apple_ref/doc/uid/TP40008193-SW1" target="_blank" rel="noopener">Apple docs&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="custom-meta-tags">
&lt;a class="heading-anchor-link" href="#custom-meta-tags">Custom meta tags&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-meta-tags"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lx:category&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;group&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lx:cid&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lx:appnm&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mtpc&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;lx:autopv&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;off&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These are custom tags. I do something similar—e.g., add a &lt;code>releaseDate&lt;/code> tag to mark build times and quickly spot which version is live.&lt;/p>
&lt;h2 id="dns-prefetching">
&lt;a class="heading-anchor-link" href="#dns-prefetching">DNS prefetching&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dns-prefetching"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//analytics.meituan.net&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//www.meituan.com&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//s0.meituan.net&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//s1.meituan.net&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//p0.meituan.net&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;dns-prefetch&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//p1.meituan.net&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ul>
&lt;li>Smart use of DNS prefetching can shave 50–300 ms from page load.&lt;/li>
&lt;li>Best practices:
&lt;ol>
&lt;li>Prefetch static asset domains manually.&lt;/li>
&lt;li>Prefetch domains used by client-side redirects or requests.&lt;/li>
&lt;li>Skip manual prefetch on plain hyperlinks—Chrome already does it (except for HTTPS, where you need &lt;code>&amp;lt;meta http-equiv=&amp;quot;x-dns-prefetch-control&amp;quot; content=&amp;quot;on&amp;quot;&amp;gt;&lt;/code>).&lt;/li>
&lt;li>Prefetch the redirect target domain when you know a link will jump there.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;p>Recommended reads:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://tech.youzan.com/dns-prefetching" target="_blank" rel="noopener">DNS Prefetching Done Right&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/barretlee/performance-column/issues/3" target="_blank" rel="noopener">Performance Optimization: DNS Prefetch&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="httphttps-switch">
&lt;a class="heading-anchor-link" href="#httphttps-switch">HTTP→HTTPS switch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="httphttps-switch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">protocol&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;http:&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http://&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;https://&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In practice this doesn’t do anything because the server already issues a 302 redirect (I’d prefer a 301).&lt;/p>
&lt;h2 id="protocol-relative-urls">
&lt;a class="heading-anchor-link" href="#protocol-relative-urls">Protocol-relative URLs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="protocol-relative-urls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/css&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//s0.meituan.net/bs/fe-web-meituan/23bb705/css/main.css&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since the server forces HTTPS, the protocol-relative form doesn’t buy much here—but it’s a handy technique elsewhere.&lt;/p>
&lt;h2 id="async-vs-defer">
&lt;a class="heading-anchor-link" href="#async-vs-defer">async vs. defer&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="async-vs-defer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//analytics.meituan.net/analytics.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/javascript&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">async&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">defer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ul>
&lt;li>&lt;code>async&lt;/code> downloads without blocking HTML parsing.&lt;/li>
&lt;li>&lt;code>defer&lt;/code> delays execution until the document is parsed.&lt;/li>
&lt;li>You can specify both—they’re simply attributes on &lt;code>&amp;lt;script&amp;gt;&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="link-inside-body">
&lt;a class="heading-anchor-link" href="#link-inside-body">&lt;code>&amp;lt;link&amp;gt;&lt;/code> inside &lt;code>&amp;lt;body&amp;gt;&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="link-inside-body"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/css&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;//s0.meituan.net/bs/fe-web-meituan/5058856/css/com_header.css&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Browsers fix up HTML before rendering, so this still works. HTML is resilient.&lt;/p>
&lt;h2 id="iife-example">
&lt;a class="heading-anchor-link" href="#iife-example">IIFE example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="iife-example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">win&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ns&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">cacheFunName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;_MeiTuanALogObject&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">win&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">cacheFunName&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ns&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">win&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">ns&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">_LX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_LX&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">_LX&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_LX&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_LX&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">q&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_LX&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">l&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">+&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">win&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">ns&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_LX&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;LXAnalytics&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>IIFEs helped emulate block scope before ES6; today &lt;code>let&lt;/code>/&lt;code>const&lt;/code> often cover the same needs.&lt;/li>
&lt;li>The leading &lt;code>!&lt;/code> is unnecessary—no return value is consumed.&lt;/li>
&lt;/ul>
&lt;p>A quick comparison illustrates the value:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">((&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Even a static HTML page surfaces a ton of front-end details. Because our craft is transparent, peek at other sites often—you’ll always learn something new.&lt;/p></description></item><item><title>Improve Frontend Code Quality — Stylelint</title><link>https://en.1991421.cn/2020/10/08/c976b46/</link><pubDate>Thu, 08 Oct 2020 21:19:59 +0800</pubDate><guid>https://en.1991421.cn/2020/10/08/c976b46/</guid><description>&lt;blockquote>
&lt;p>The three pillars of frontend development are HTML, CSS, and JS. Whether you’re building an SPA or an MPA, there’s relatively less HTML, so style enforcement mostly focuses on JS and CSS. For JS we have ESLint; for CSS, I recommend Stylelint.&lt;/p>
&lt;/blockquote>
&lt;p>Here’s a quick setup guide.&lt;/p>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="install-packages">
&lt;a class="heading-anchor-link" href="#install-packages">Install packages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-packages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ yarn add stylelint-config-standard stylelint -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="config-file">
&lt;a class="heading-anchor-link" href="#config-file">Config file&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="config-file"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>.stylelintrc.json&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;extends&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;stylelint-config-standard&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rules&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;number-leading-zero&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;never&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;selector-pseudo-class-no-unknown&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ignorePseudoClasses&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;/global/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="packagejson-scripts">
&lt;a class="heading-anchor-link" href="#packagejson-scripts">package.json scripts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packagejson-scripts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;lint-less&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;yarn run stylelint \&amp;#34;src/main/webapp/**/*.less\&amp;#34; --syntax=less&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;lint-less:fix&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;yarn run lint-less --fix&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;lint-staged&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;*.less&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;stylelint --syntax=less --fix&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;prettier --write&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;git add&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes&lt;/p>
&lt;ol>
&lt;li>The lint-staged configuration ensures only staged files are checked at commit time, not the whole project.&lt;/li>
&lt;li>You can omit &lt;code>syntax&lt;/code> — the CLI can auto-detect — but I prefer being explicit.&lt;/li>
&lt;/ol>
&lt;h2 id="whats-next">
&lt;a class="heading-anchor-link" href="#whats-next">What’s next?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="whats-next"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above is just a quick start. In real projects, adjust or override rules as needed. For multi-project setups, consider publishing a private shared config and using &lt;code>extends&lt;/code>. Keep iterating and refining during development.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Keep in mind: Stylelint is very similar to ESLint — same philosophy, just for a different language.&lt;/li>
&lt;li>Linters enforce style so teams with mixed experience still converge on consistent code. You can even skip separate style docs since violations will be flagged with friendly messages.&lt;/li>
&lt;li>Value consistency; value rules.&lt;/li>
&lt;/ul></description></item><item><title>Error Boundaries Explained (Simple Guide)</title><link>https://en.1991421.cn/2020/10/04/error-boundaries/</link><pubDate>Sun, 04 Oct 2020 20:27:08 +0800</pubDate><guid>https://en.1991421.cn/2020/10/04/error-boundaries/</guid><description>&lt;blockquote>
&lt;p>React文档中讲到一个概念-Error Boundaries（错误边界），今天正好在读一本书&lt;a href="https://book.douban.com/subject/34887887/" target="_blank" rel="noopener">《Learning React》&lt;/a>,借着刚读完的熟悉感 + 结合实际的项目，聊下个人的实践认知。&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-problems-they-solve">
&lt;a class="heading-anchor-link" href="#what-problems-they-solve">What problems they solve&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-problems-they-solve"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an “error boundary”.&lt;/p>
&lt;/blockquote>
&lt;p>如上为官网开篇的一段介绍，由此可以看出该技术的根本目标是希望UI层的异常不要导致整体的App的崩溃。&lt;/p>
&lt;p>注意&lt;/p>
&lt;ul>
&lt;li>
&lt;p>仅限UI层&lt;/p>
&lt;/li>
&lt;li>
&lt;p>对于这种处理实际上，可以看作我们函数中经常写的&lt;code>try catch&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>React开发模式打包，出现了运行异常，在显示我们既定的错误边界render外，还是会展示堆栈错误信息&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-04-212106.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="out-of-scope">
&lt;a class="heading-anchor-link" href="#out-of-scope">Out of scope&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="out-of-scope"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>比如我当前的项目，技术栈为React+Redux+Redux-Saga+Axios等，假如是在Saga-effects中出现了未捕获的异常，实际上也会造成whole app的崩溃，Error Boundaries是无法解决这个问题的，因为不在UI层。那怎么办呢，就是针对性对saga要做try catch,比如做个高阶函数-safe-wrapper。&lt;/p>
&lt;h2 id="practical-usage">
&lt;a class="heading-anchor-link" href="#practical-usage">Practical usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practical-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>这里没什么好写的，官网&lt;a href="https://reactjs.org/docs/error-boundaries.html#gatsby-focus-wrapper" target="_blank" rel="noopener">例子&lt;/a>足矣。当然需要注意的是，因为这种设计，实际上我们也可以很方便的捕获了UI层的错误，发送到issue📱服务上。对于Web产品运维，这还是很重要的。&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>在实际开发中，越来越喜欢React，因为足够的简单，足够的纯粹。但同时，在这个simple design的Lib下，也学到了很多，比如设计模式，比如编程范式等等。&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://reactjs.org/docs/error-boundaries.html#gatsby-focus-wrapper" target="_blank" rel="noopener">React Official - Error Boundaries&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Jenkins Continuous Deployment - Email Notification with ChangeLog</title><link>https://en.1991421.cn/2020/10/02/77734821/</link><pubDate>Fri, 02 Oct 2020 14:31:08 +0800</pubDate><guid>https://en.1991421.cn/2020/10/02/77734821/</guid><description>&lt;blockquote>
&lt;p>To enable timely notifications to BA and Dev colleagues after production releases, I spent some time researching Jenkins over the weekend. Here&amp;rsquo;s a simple markdown summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation-and-configuration">
&lt;a class="heading-anchor-link" href="#installation-and-configuration">Installation and Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation-and-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="plugin-manager">
&lt;a class="heading-anchor-link" href="#plugin-manager">Plugin Manager&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugin-manager"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The built-in mail plugin is too simple. To achieve email template customization, the following plugins need to be installed:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://plugins.jenkins.io/email-ext/" target="_blank" rel="noopener">Email Extension Plugin&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://plugins.jenkins.io/config-file-provider/" target="_blank" rel="noopener">Config File Provider&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="jenkins-configuration">
&lt;a class="heading-anchor-link" href="#jenkins-configuration">Jenkins Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jenkins-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The Extension plugin is different from the built-in mail plugin and requires separate email server configuration&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-02-143406.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="template-creation">
&lt;a class="heading-anchor-link" href="#template-creation">Template Creation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="template-creation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Instead of directly logging into the Jenkins deployment server for file operations, you can use the GUI directly. Here I chose the GUI approach:&lt;/p>
&lt;p>Managed files =&amp;gt; Add a new Config =&amp;gt; Extended Email Publisher Groovy Template&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-02-143643.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Notes&lt;/p>
&lt;ul>
&lt;li>This configuration will be in the service root directory, use &amp;lsquo;groovy-html.template&amp;rsquo; directly when needed&lt;/li>
&lt;li>The template I chose here is based on modifications of the official plugin&amp;rsquo;s provided templates. Plugin templates are not installed by default, so manual configuration is required:
&lt;ul>
&lt;li>Plugin template download &lt;a href="https://github.com/jenkinsci/email-ext-plugin/tree/master/docs/templates" target="_blank" rel="noopener">click here&lt;/a>&lt;/li>
&lt;li>My improved template for sending Git ChangeLog &lt;a href="https://gist.github.com/alanhe421/b577d5a30ae5b16e9404cdf6624895b3" target="_blank" rel="noopener">click here&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="pipeline-configuration">
&lt;a class="heading-anchor-link" href="#pipeline-configuration">Pipeline Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pipeline-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-groovy" data-lang="groovy">&lt;span class="line">&lt;span class="cl">&lt;span class="n">properties&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="n">buildDiscarder&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">logRotator&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nl">artifactNumToKeepStr:&lt;/span> &lt;span class="s1">&amp;#39;10&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nl">numToKeepStr:&lt;/span> &lt;span class="s1">&amp;#39;10&amp;#39;&lt;/span>&lt;span class="o">))])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">node&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">currentBuild&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;SUCCESS&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="n">err&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">currentBuild&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;FAILURE&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">finally&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">stage&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="s1">&amp;#39;Notify&amp;#39;&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">emailext&lt;/span> &lt;span class="nl">to:&lt;/span> &lt;span class="s1">&amp;#39;alan@1991421.cn&amp;#39;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">recipientProviders:&lt;/span> &lt;span class="o">[[&lt;/span>&lt;span class="n">$class&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;RequesterRecipientProvider&amp;#39;&lt;/span>&lt;span class="o">],[&lt;/span>&lt;span class="n">$class&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;DevelopersRecipientProvider&amp;#39;&lt;/span>&lt;span class="o">]],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">subject:&lt;/span> &lt;span class="s2">&amp;#34;Production deployment: ${currentBuild.fullDisplayName} ${currentBuild.result}&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">body:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;${SCRIPT,template=&amp;#34;managed:groovy-html.template&amp;#34;}&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">mimeType:&lt;/span> &lt;span class="s1">&amp;#39;text/html&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="notes">Notes&lt;/h4>&lt;ol>
&lt;li>try catch should be at the outermost level&lt;/li>
&lt;li>mimeType is recommended to be explicitly specified, as in some versions it may cause emails to be sent as HTML text without proper rendering&lt;/li>
&lt;/ol>
&lt;h2 id="results">
&lt;a class="heading-anchor-link" href="#results">Results&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="results"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The following is the final email sent:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-10-02-145255.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>It&amp;rsquo;s important to know that even if the final notification indicates a successful release, if it&amp;rsquo;s a Java service, the container startup actually takes time. Depending on the specific container service configuration, the effective delay will vary. So strictly speaking, a successful email notification doesn&amp;rsquo;t necessarily mean it&amp;rsquo;s &lt;code>simultaneously effective and online&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Personally, I think the documentation for Jenkins and its surrounding plugins is quite average. Comparatively speaking, looking at practical books is more efficient than reading official documentation and Googling&lt;/li>
&lt;li>I recommend the book I&amp;rsquo;m currently reading &lt;a href="https://learning.oreilly.com/library/view/jenkins-2-up/9781491979587/ch04.html#CH_Notifications_and_Reports" target="_blank" rel="noopener">《Jenkins 2: Up and Running》&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://learning.oreilly.com/library/view/jenkins-2-up/9781491979587/ch04.html#CH_Notifications_and_Reports" target="_blank" rel="noopener">Jenkins 2: Up and Running&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jenkinsci/email-ext-plugin" target="_blank" rel="noopener">email-ext-plugin&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>JavaScript Event Loop</title><link>https://en.1991421.cn/2020/09/26/js-concurrency-event-loop/</link><pubDate>Sat, 26 Sep 2020 16:31:03 +0800</pubDate><guid>https://en.1991421.cn/2020/09/26/js-concurrency-event-loop/</guid><description>&lt;blockquote>
&lt;p>I recently saw a basic question and realized my understanding of the JS event loop was not solid. So I organized it here to reinforce the fundamentals.&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-basic-question">
&lt;a class="heading-anchor-link" href="#the-basic-question">The basic question&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-basic-question"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here is the original question:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;children2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;children3&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;children4&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;children5&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;children6&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;children7&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="correct-answer">
&lt;a class="heading-anchor-link" href="#correct-answer">Correct answer&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="correct-answer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">children4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">children2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">children3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">children5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">children7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">children6
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="explanation">
&lt;a class="heading-anchor-link" href="#explanation">Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Macro task prints &lt;code>start&lt;/code>&lt;/li>
&lt;li>setTimeout enters the EventTable and registers, timer starts&lt;/li>
&lt;li>Promise executor runs immediately, prints &lt;code>children4&lt;/code>, then setTimeout enters EventTable. First round ends&lt;/li>
&lt;li>First timer enters queue, prints &lt;code>children2&lt;/code>, promise.then added to micro queue, prints &lt;code>children3&lt;/code>&lt;/li>
&lt;li>Second timer runs, prints &lt;code>children5&lt;/code>, promise.then added to micro queue, prints &lt;code>children7&lt;/code>&lt;/li>
&lt;li>Timer fires, added to macro task, prints &lt;code>children6&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="key-concepts">
&lt;a class="heading-anchor-link" href="#key-concepts">Key concepts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-concepts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>JS is single-threaded. The event loop is JS&amp;rsquo;s execution mechanism, and other languages differ (for example, Java supports multithreading).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Synchronous and asynchronous tasks go into different execution contexts.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-09-26-193331.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Async tasks can be divided into macro tasks and micro tasks.&lt;/p>
&lt;p>Example: A bank clerk handling customers is a macro task. If a customer adds a small extra request, that can be considered a micro task.&lt;/p>
&lt;p>Each macro task has a micro task queue. After each macro task finishes, the micro tasks are drained before the next macro task.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-09-26-191836.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Macro tasks: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering&lt;/p>
&lt;p>Micro tasks: process.nextTick (Node), Promises, queueMicrotask, MutationObserver&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Even with 0 delay, setTimeout does not run immediately. Only after the call stack is empty will tasks be pulled from the Event Queue.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Promise represents the final completion (or failure) of an async operation and its result.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The event loop consists of the main thread and task queue. The main thread keeps pulling tasks from the queue and executing them.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="practice-questions">
&lt;a class="heading-anchor-link" href="#practice-questions">Practice questions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practice-questions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="q1">
&lt;a class="heading-anchor-link" href="#q1">Q1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;em>NOTE: Run in Node.js.&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nextTick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;3&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;4&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;5&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nextTick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;6&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;7&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;8&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;9&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nextTick&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;10&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;11&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;12&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="correct-answer-1">Correct answer&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">10
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">12
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="explanation-1">Explanation&lt;/h4>&lt;ul>
&lt;li>
&lt;p>Macro task prints &lt;code>1&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>setTimeouts go into the delay queue&lt;/p>
&lt;/li>
&lt;li>
&lt;p>process.nextTick creates a micro task (not executed yet)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Promise executor runs immediately, prints &lt;code>7&lt;/code>, .then creates a micro task&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Another setTimeout goes into the delay queue&lt;/p>
&lt;/li>
&lt;li>
&lt;p>No more sync code, run micro tasks: &lt;code>6&lt;/code>, &lt;code>8&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Task stack cleared, timers are ready, move to message queue and execute&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Print &lt;code>2&lt;/code>, &lt;code>4&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Run micro tasks: &lt;code>3&lt;/code>, &lt;code>5&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Print &lt;code>9&lt;/code>, &lt;code>11&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Run micro tasks: &lt;code>10&lt;/code>, &lt;code>12&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="q2">
&lt;a class="heading-anchor-link" href="#q2">Q2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;script start&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;setTimeout&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;promise1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;promise2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;script end&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="correct-answer-2">Correct answer&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">script start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">script end
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">promise1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">promise2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">setTimeout
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="explanation-2">Explanation&lt;/h4>&lt;ul>
&lt;li>Macro task prints &lt;code>script start&lt;/code>&lt;/li>
&lt;li>Timer goes into event table&lt;/li>
&lt;li>promise.then is a micro task, not executed until macro task ends&lt;/li>
&lt;li>Macro task prints &lt;code>script end&lt;/code>&lt;/li>
&lt;li>Macro task ends, run micro tasks: &lt;code>promise1&lt;/code>, &lt;code>promise2&lt;/code>&lt;/li>
&lt;li>Micro tasks end, timer fires, go to event queue, execute and print &lt;code>setTimeout&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="q3">
&lt;a class="heading-anchor-link" href="#q3">Q3&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="q3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">foo&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">foo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">foo&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">foo&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">foo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">foo&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Snippet 1 will not overflow the stack, but snippet 2 will.&lt;/p>
&lt;h4 id="explanation-3">Explanation&lt;/h4>&lt;ul>
&lt;li>setTimeout is a macro task, Promise is a micro task. Macro tasks are pushed one per loop cycle, but micro tasks are always drained before returning to the event loop. If you keep adding micro tasks faster than you process them, the loop never returns to rendering and you will overflow.&lt;/li>
&lt;/ul>
&lt;h2 id="non-blocking">
&lt;a class="heading-anchor-link" href="#non-blocking">Non-blocking?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="non-blocking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we understand the event loop and concurrency model, let&amp;rsquo;s mention non-blocking. When talking about Node.js, people often mention async non-blocking.&lt;/p>
&lt;p>Note: async and non-blocking are two concepts. JS is single-threaded, so only one thing runs at a time. To improve responsiveness, async exists. Async means you can continue without waiting for results, so it is non-blocking.&lt;/p>
&lt;p>What is blocking? For example, in the browser, if you call alert, nothing else can happen until the user clicks OK. That is blocking.&lt;/p>
&lt;p>Note that async non-blocking is a JS feature. Browser JS also has it.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This post is rough. If there are issues, please point them out.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop" target="_blank" rel="noopener">Concurrency model and the event loop
&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/6844903638238756878#heading-8" target="_blank" rel="noopener">JS event loop: macro tasks / micro tasks
&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/6844903657264136200" target="_blank" rel="noopener">Microtasks, macrotasks and the Event Loop&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/javascript-in-plain-english/you-dont-know-javascript-until-you-can-beat-this-game-aa7fd58befb" target="_blank" rel="noopener">You don&amp;rsquo;t know JavaScript until you can beat this game&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ&amp;amp;feature=youtu.be&amp;amp;ab_channel=JSConf" target="_blank" rel="noopener">What the heck is the event loop anyway? | Philip Roberts | JSConf EU&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Project Code Statistics</title><link>https://en.1991421.cn/2020/09/24/code-line-statistics/</link><pubDate>Thu, 24 Sep 2020 01:04:57 +0800</pubDate><guid>https://en.1991421.cn/2020/09/24/code-line-statistics/</guid><description>&lt;blockquote>
&lt;p>I was asked to measure the total code size across all project repos. This write-up documents the process I automated.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tasking">
&lt;a class="heading-anchor-link" href="#tasking">Tasking&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tasking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To collect code statistics:&lt;/p>
&lt;ol>
&lt;li>Batch-clone every repo in the group using &lt;a href="https://github.com/gabrie30/ghorg" target="_blank" rel="noopener">ghorg&lt;/a>.&lt;/li>
&lt;li>Run &lt;a href="https://github.com/kentcdodds/cloc" target="_blank" rel="noopener">cloc&lt;/a> for each repo.&lt;/li>
&lt;li>Merge the CSV outputs.&lt;/li>
&lt;/ol>
&lt;h2 id="code">
&lt;a class="heading-anchor-link" href="#code">Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Steps:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Clone repositories&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">ghorg clone groupname --concurrency&lt;span class="o">=&lt;/span>&lt;span class="m">50&lt;/span> --protocol&lt;span class="o">=&lt;/span>ssh --scm&lt;span class="o">=&lt;/span>gitlab --base-url&lt;span class="o">=&lt;/span>https://gitlab.com --token&lt;span class="o">=&lt;/span>CP-SevCew54pjq_JnuDb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Install ghorg per the docs.&lt;/li>
&lt;li>The token is your GitLab access token.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Generate reports&lt;/p>
&lt;ul>
&lt;li>Install cloc: &lt;code>npm i cloc&lt;/code>&lt;/li>
&lt;li>Script below iterates over repos, runs cloc, and merges reports. Configure the four path variables before running.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">fs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;fs&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">execSync&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;child_process&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Path to the cloned repositories
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">rootDir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// cloc executable
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">clocCommand&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/Users/qhe/.nvm/versions/node/v10.16.0/bin/cloc&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Temporary directory for individual reports
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">reportFiles&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Final merged report path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">reportFile&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">deleteFolderRecursive&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">existsSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readdirSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">curPath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">path&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">file&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lstatSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">curPath&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">isDirectory&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">deleteFolderRecursive&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">curPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unlinkSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">curPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">rmdirSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @description
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Generate a report for each repository
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">createReport&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rootDir&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">_0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">files&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">files&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">statSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">rootDir&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">isFile&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">execSync&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">clocCommand&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">rootDir&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb"> --csv --out=out/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">.csv`&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">stdout&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">stderr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`error: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">stderr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`stderr: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">stderr&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`stdout: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">stdout&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @description
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Merge repository reports into a single CSV file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">mergeReport&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">mergedReport&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">reportFiles&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">_0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">files&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">files&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;.DS_Store&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">contents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readFileSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">reportFiles&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoding&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mergedReport&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/\.csv/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">,,,,,\n`&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">contents&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">deleteFolderRecursive&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">reportFiles&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">reportFile&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mergedReport&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createReport&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">mergeReport&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-09-24-011351.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>With filters in Excel I can quickly explore the data: 34 repos totaling 1,340,244 lines (including vendor code).&lt;/li>
&lt;li>Note: running cloc on the parent directory vs. summing repo-level runs can differ. I trust the per-repo numbers.&lt;/li>
&lt;/ul>
&lt;h2 id="closing-thoughts">
&lt;a class="heading-anchor-link" href="#closing-thoughts">Closing Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closing-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the script in place, future runs take only two commands.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/gabrie30/ghorg" target="_blank" rel="noopener">https://github.com/gabrie30/ghorg&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/kentcdodds/cloc" target="_blank" rel="noopener">https://github.com/kentcdodds/cloc&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Pro Git Reading Notes</title><link>https://en.1991421.cn/2020/08/22/7285054/</link><pubDate>Sat, 22 Aug 2020 14:32:16 +0800</pubDate><guid>https://en.1991421.cn/2020/08/22/7285054/</guid><description>&lt;blockquote>
&lt;p>Git has become an indispensable skill in development, and mastering Git is a productivity booster. I had some knowledge gaps about Git, so I spent some fragmented time reading &lt;a href="https://book.douban.com/subject/26208470" target="_blank" rel="noopener">&amp;ldquo;Pro Git, Second Edition&amp;rdquo;&lt;/a>. It&amp;rsquo;s indeed an excellent book, as recommended by GitHub officially.&lt;/p>
&lt;/blockquote>
&lt;p>Here are some key points I&amp;rsquo;m noting down to reinforce my memory:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-22-150715.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="git-commit-modification">
&lt;a class="heading-anchor-link" href="#git-commit-modification">Git Commit Modification&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="git-commit-modification"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>If it&amp;rsquo;s only a local commit that hasn&amp;rsquo;t been pushed yet, you can directly modify the commit message using the principle of &lt;code>git commit --amend -m&lt;/code>&lt;/li>
&lt;li>If it&amp;rsquo;s the last commit that has already been pushed to the remote, you can reset the pointer to the previous commit and then recommit, but this requires &lt;code>--force&lt;/code> when pushing&lt;/li>
&lt;/ul>
&lt;h3 id="important-notes">
&lt;a class="heading-anchor-link" href="#important-notes">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>After modifying the commit message, the hash value will change. Any recommit will change the hash value&lt;/li>
&lt;li>If you want to modify a commit that&amp;rsquo;s not the most recent one, it&amp;rsquo;s possible with local history, but not for commits that have already been pushed&lt;/li>
&lt;/ol>
&lt;p>Overall, history cannot be changed, but the above scenarios allow for modifications.&lt;/p>
&lt;h2 id="rebase-vs-merge">
&lt;a class="heading-anchor-link" href="#rebase-vs-merge">Rebase vs Merge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rebase-vs-merge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Our Git workflow often involves multiple branches, and for merging changes between branches, there are two approaches: Merge and Rebase.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-22-143747.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Merge effect as shown, note:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-22-143804.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Rebase - changing the base, effect as shown:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-22-143822.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="important-notes-1">
&lt;a class="heading-anchor-link" href="#important-notes-1">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Merge creates a new commit&lt;/li>
&lt;li>Rebase doesn&amp;rsquo;t create additional commits&lt;/li>
&lt;li>The specific Rebase operation involves going to the temp branch, executing rebase master to handle conflicts, which changes the ancestor to master, and then performing a merge on the master branch&lt;/li>
&lt;li>Rebase changes the base, meaning it changes the pointer to the ancestor&lt;/li>
&lt;/ul>
&lt;h2 id="server-side-git-hooks">
&lt;a class="heading-anchor-link" href="#server-side-git-hooks">Server-side Git Hooks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="server-side-git-hooks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Git hooks can trigger custom scripts to execute at specific behavioral moments.&lt;/p>
&lt;/blockquote>
&lt;p>In actual project development, client-side Git hooks have been playing an important role, such as linting code style, enforcing local unit tests, etc. However, it&amp;rsquo;s important to know that hooks also have server-side support. This complements the limitations of client-side hooks, allowing for things like commit message quality checks, rejecting invalid pushes, etc.&lt;/p>
&lt;p>Server-side Git hooks configuration principles are similar to local ones - remember that Git is distributed.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The &amp;ldquo;Pro Git&amp;rdquo; book provides comprehensive and practical insights into Git&amp;rsquo;s internals and best practices. Understanding these concepts - commit modification strategies, the differences between rebase and merge, and server-side hooks - can significantly improve your Git workflow efficiency and collaboration capabilities. These fundamentals are essential for any developer working with version control systems.&lt;/p></description></item><item><title>Practical Tips for Using GitLab</title><link>https://en.1991421.cn/2020/08/21/gitlab/</link><pubDate>Fri, 21 Aug 2020 22:07:29 +0800</pubDate><guid>https://en.1991421.cn/2020/08/21/gitlab/</guid><description>&lt;blockquote>
&lt;p>Our company has been using GitLab for code management, and in practice, we&amp;rsquo;ve accumulated various tips and techniques that I&amp;rsquo;d like to document here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-21-222322.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="gitlab---rich-apis">
&lt;a class="heading-anchor-link" href="#gitlab---rich-apis">GitLab - Rich APIs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gitlab---rich-apis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Leveraging these APIs can accomplish many tasks, such as using API + pipeline to create automated Merge Requests.&lt;/p>
&lt;p>For example, we use Jenkins to regularly run changelog submissions to the repository, which also relies on GitLab API.&lt;/p>
&lt;p>&lt;a href="https://docs.gitlab.com/ee/api/" target="_blank" rel="noopener">API Documentation&lt;/a> - make sure to check your specific installation version&lt;/p>
&lt;h2 id="pipeline">
&lt;a class="heading-anchor-link" href="#pipeline">Pipeline&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pipeline"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>GitLab provides CI+CD capabilities. While Jenkins itself is also a CD tool, our team&amp;rsquo;s current practice is to use GitLab only for CI (running unit tests), and use hooks to trigger Jenkins for CD deployments across different environments.&lt;/p>
&lt;p>Note that CI/CD configuration files themselves have directive support, so you can share configuration code blocks across multiple projects.&lt;/p>
&lt;h2 id="templates">
&lt;a class="heading-anchor-link" href="#templates">Templates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="templates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For MRs, Issues, etc., we can create templates. For example, in my team, we use a rotation system for Merge Owners, but remembering who it is today can be tedious. Therefore, we created templates so that each time, when reviewing an MR, checking the MR Guide will show who it is today.&lt;/p>
&lt;h2 id="commit-syntax-sugar">
&lt;a class="heading-anchor-link" href="#commit-syntax-sugar">Commit Syntax Sugar&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="commit-syntax-sugar"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Enabling pipelines is indeed great for ensuring merge code safety, but what if sometimes I want to merge directly without going through the pipeline? OK, GitLab provides syntax sugar &lt;code>[skip ci]&lt;/code>, but generally avoid using it.&lt;/p>
&lt;p>Besides this, there are also commands like &lt;code>Fixes #45&lt;/code>, etc.&lt;/p></description></item><item><title>Improving Code Quality — Standardizing Git Commits</title><link>https://en.1991421.cn/2020/08/08/normalize-git-commit-message/</link><pubDate>Sat, 08 Aug 2020 12:20:27 +0800</pubDate><guid>https://en.1991421.cn/2020/08/08/normalize-git-commit-message/</guid><description>&lt;blockquote>
&lt;p>In earlier posts I covered &lt;code>commitlint&lt;/code> and &lt;code>standard-version&lt;/code> for enforcing commit messages and generating clean changelogs. The format still requires manual discipline though. Wouldn’t it be nicer to fill out a form? Thankfully an open-source plugin exists.&lt;/p>
&lt;/blockquote>
&lt;h2 id="commit-template-idea-plugin">
&lt;a class="heading-anchor-link" href="#commit-template-idea-plugin">commit-template-idea-plugin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="commit-template-idea-plugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-08-124349.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-08-122813.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Things to note:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>How to fill each field? Plenty of guides exist, but here’s my shorthand:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>type&lt;/strong> — choose carefully. Click into the preset list to read each description. &lt;code>style&lt;/code> refers to code style, not CSS.&lt;/li>
&lt;li>&lt;strong>scope&lt;/strong> — no strict rules; use a business module or a technical area.&lt;/li>
&lt;li>&lt;strong>description&lt;/strong> — the imperative summary of what changed, e.g., &lt;code>set button background to red&lt;/code>.&lt;/li>
&lt;li>&lt;strong>long description&lt;/strong> — an extended explanation.&lt;/li>
&lt;li>&lt;strong>closed issue&lt;/strong> — we usually link to tickets; make this mandatory (Jira, GitLab issue, etc.).&lt;/li>
&lt;/ul>
&lt;p>Highlight: the &lt;code>type&lt;/code> determines semantic-version bumps. &lt;code>breaking change&lt;/code> increments the major version, &lt;code>feat&lt;/code> bumps the minor, &lt;code>fix&lt;/code> bumps the patch. That explains why dependencies sometimes use &lt;code>~&lt;/code>, &lt;code>^&lt;/code>, or exact versions—semver underpins it all.&lt;/p>
&lt;p>Google’s practice for descriptions is to imagine you’re issuing a command to the computer; write commits in that style.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The plugin offers two actions—the first opens the form, the second (highlighted in the screenshot) lists recent commits so you can reuse and tweak them.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Commit history is stored per project, not globally.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Prefix issue IDs with &lt;code>#&lt;/code>; otherwise &lt;code>standard-version&lt;/code> can’t link them in the changelog.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="current-limitations">
&lt;a class="heading-anchor-link" href="#current-limitations">Current limitations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-limitations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Projects can define custom change types in &lt;code>.versionrc.json&lt;/code>, but the plugin doesn’t read that config.&lt;/li>
&lt;li>After you submit, the plugin composes a plain-text commit message. There’s no reverse parsing—if you want to edit, tweak the text directly or reopen the form and start over.&lt;/li>
&lt;/ol>
&lt;h2 id="an-example-commit">
&lt;a class="heading-anchor-link" href="#an-example-commit">An example commit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="an-example-commit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-08-125743.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Angular’s commit style is my personal benchmark. Remember a complete commit also depends on &lt;code>user.name&lt;/code> and &lt;code>user.email&lt;/code>. I’ve seen colleagues produce three or four different author identities because they work across repos with different Git configs. Git supports conditional includes so you can tailor settings per directory—details &lt;a href="https://1991421.cn/2020/08/08/4ead3715/" target="_blank" rel="noopener">here&lt;/a>.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Many people downplay commit messages, but they capture history. When reporting iteration progress, a good changelog tells you how many bugs were fixed, which features shipped, and which tickets they map to. Without discipline you’ll be digging manually and still miss items. Automation and conventions save the most time.&lt;/li>
&lt;li>The plugin has gaps, but it’s open source. If you have the motivation, contribute or customize—it’s there for the taking.&lt;/li>
&lt;/ul>
&lt;p>Commit → changelog → semver. Clean commits produce trustworthy changelogs, which power semantic versioning.&lt;/p></description></item><item><title>Git Configuration for Using Different Settings Based on Multiple Git Services</title><link>https://en.1991421.cn/2020/08/08/gitgit/</link><pubDate>Sat, 08 Aug 2020 12:01:18 +0800</pubDate><guid>https://en.1991421.cn/2020/08/08/gitgit/</guid><description>&lt;blockquote>
&lt;p>Sometimes, different Git configurations are needed for personal GitHub projects and company GitLab projects. For example, I want to use my company username, heqiangx, for GitLab projects but my English name, Alan He, for GitHub. How can this be achieved? The solution is to switch Git configurations automatically based on the repository address.&lt;/p>
&lt;/blockquote>
&lt;p>Manually configuring each repository? Sorry, I dislike inefficient methods.&lt;/p>
&lt;h2 id="sshconfig">
&lt;a class="heading-anchor-link" href="#sshconfig">&lt;del>.ssh/config&lt;/del>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sshconfig"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Why mention this file first? I initially misunderstood its purpose. Previously, I relied on the configurations here to ensure commits to different repository services used different tokens, leading me to believe this file could solve the problem.&lt;/p>
&lt;p>However, this is incorrect because the core issue is:&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>.ssh/config&lt;/code> addresses authentication, as Git uses SSH for authentication. However, my need for personalized Git settings isn&amp;rsquo;t related to authentication but rather to Git configurations, which must be resolved using Git&amp;rsquo;s configuration files.&lt;/p>
&lt;/blockquote>
&lt;h2 id="gitconfig">
&lt;a class="heading-anchor-link" href="#gitconfig">.gitconfig&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gitconfig"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="main-configuration">
&lt;a class="heading-anchor-link" href="#main-configuration">Main Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="main-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Note: open the configuration file in the &lt;code>~&lt;/code> directory and paste the following sections. Once saved, Git operations will follow different personalized configurations based on the repository&amp;rsquo;s path.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">// ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[includeIf &amp;#34;gitdir:~/Documents/GitHub/&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> path = ~/.gitconfig-personal
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[includeIf &amp;#34;gitdir:~/Documents/GitLab/&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> path = ~/.gitconfig-work
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="personalized-configuration">
&lt;a class="heading-anchor-link" href="#personalized-configuration">Personalized Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="personalized-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Below is the &lt;code>.gitconfig-personal&lt;/code> configuration. The &lt;code>.gitconfig-work&lt;/code> is similar, differing only in the &lt;code>name&lt;/code> and &lt;code>email&lt;/code> fields.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[user]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> name = Alan He
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> email = alan@1991421.cn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The solution above offers a one-time setup to ensure all projects under different directories follow specific Git configurations. However, note that these are &amp;ldquo;global configurations.&amp;rdquo; If a repository has its particular settings, it will override these configurations. To remove the repository-specific settings and use the global configurations, you can do the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">// Remove the current user.name configuration
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config --unset user.name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// List all current configurations in the repository
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config --list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Personalized Git configurations for individual repositories are rare or unnecessary. My primary need is to separate configurations for company and personal projects.&lt;/li>
&lt;li>The solution above is highly valuable because it enables multiple global configurations to apply automatically based on the type of project.&lt;/li>
&lt;li>Of course, you could configure each project individually if needed, but the effort involved would be significantly higher. Thus, the best approach is to choose the most suitable method based on the situation.&lt;/li>
&lt;/ul></description></item><item><title>CI Optimization - Automated Merge Request Creation</title><link>https://en.1991421.cn/2020/08/02/ci-mr/</link><pubDate>Sun, 02 Aug 2020 15:45:22 +0800</pubDate><guid>https://en.1991421.cn/2020/08/02/ci-mr/</guid><description>&lt;blockquote>
&lt;p>In practical project branch management, we often have this requirement: when we fix a bug directly against production, and the production branch is confirmed OK, we still need to manually create merge requests to other branches. As the number of branch environments increases, this becomes increasingly tedious because it&amp;rsquo;s purely manual labor. &lt;code>Where there's manual work, there will be mistakes&lt;/code>. So, can this process be automated?&lt;/p>
&lt;/blockquote>
&lt;h2 id="branch-automation-concept">
&lt;a class="heading-anchor-link" href="#branch-automation-concept">Branch Automation Concept&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="branch-automation-concept"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-08-02-154614.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Brief introduction to the environments (branches) shown in the diagram:&lt;/p>
&lt;ol>
&lt;li>master - our production environment&lt;/li>
&lt;li>uat - business testing environment&lt;/li>
&lt;li>sit - internal team tester environment&lt;/li>
&lt;li>dit - development integration environment where we work&lt;/li>
&lt;/ol>
&lt;p>The current workflow is: if we fix a bug based on master (production), this fix/456 will be manually merged to master, but then this code still needs to be merged to uat =&amp;gt; sit =&amp;gt; dit, eventually flowing into the DIT development trunk, because these branches actually have an inclusion relationship. This creates repetitive work for developers or operations personnel.&lt;/p>
&lt;p>As mentioned initially, manual work inevitably leads to mistakes. Over time, the likely tragedy with such an operational process is forgetting to create MRs - meaning lost code. Therefore, automation is essential.&lt;/p>
&lt;h2 id="automation-implementation">
&lt;a class="heading-anchor-link" href="#automation-implementation">Automation Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="automation-implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Over the weekend, I completed this automation for our team&lt;/p>
&lt;h3 id="feasibility-basis">
&lt;a class="heading-anchor-link" href="#feasibility-basis">Feasibility Basis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="feasibility-basis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>GitLab itself provides APIs to create merge requests&lt;/li>
&lt;li>CI runtime is in Linux node environment, allowing shell scripts to make MR requests&lt;/li>
&lt;/ul>
&lt;p>Here are the implementation scripts and CI configuration:&lt;/p>
&lt;p>.gitlab-ci.yml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">xxxxx/alpine:latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">variables&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">PRIVATE_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">NcWQWe9nLhxgrkNEg1Zs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># GitLab account private token&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">stages&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">openMr&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">cache&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">paths&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">node_modules&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Merge UAT to SIT&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">openMr&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;xxxxx/node:10.16.0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">before_script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">stage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">openMr&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">variables&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">TARGET_BRANCH&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sit&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">only&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">/^uat\/*/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># We have a very strict naming convention&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">except&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">variables&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">$CI_COMMIT_MESSAGE =~ /Merge branch &amp;#39;sit&amp;#39; into &amp;#39;uat&amp;#39;/&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">chmod +x ./scripts/merge-request.sh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">HOST=${CI_PROJECT_URL} CI_PROJECT_ID=${CI_PROJECT_ID} CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME} GITLAB_USER_ID=${GITLAB_USER_ID} PRIVATE_TOKEN=${PRIVATE_TOKEN} TARGET_BRANCH=${TARGET_BRANCH} ./scripts/merge-request.sh &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>merge-request.sh&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#!/usr/bin/env bash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Extract the host where the server is running, and add the URL to the APIs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[[&lt;/span> &lt;span class="nv">$HOST&lt;/span> &lt;span class="o">=&lt;/span>~ ^https?://&lt;span class="o">[&lt;/span>^/&lt;span class="o">]&lt;/span>+ &lt;span class="o">]]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nv">HOST&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">BASH_REMATCH&lt;/span>&lt;span class="p">[0]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/api/v4/projects/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The description of our new MR, we want to remove the branch after the MR has&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># been closed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BODY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;id\&amp;#34;: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">CI_PROJECT_ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;source_branch\&amp;#34;: \&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">CI_COMMIT_REF_NAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;target_branch\&amp;#34;: \&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">TARGET_BRANCH&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;remove_source_branch\&amp;#34;: true,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;title\&amp;#34;: \&amp;#34;WIP: Merge &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">CI_COMMIT_REF_NAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> to &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">TARGET_BRANCH&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> \&amp;#34;assignee_id\&amp;#34;:\&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">GITLAB_USER_ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">}&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Require a list of all the merge request and take a look if there is already&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># one with the same source branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">LISTMR&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sb">`&lt;/span>curl --silent &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">HOST&lt;/span>&lt;span class="si">}${&lt;/span>&lt;span class="nv">CI_PROJECT_ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/merge_requests?state=opened&amp;#34;&lt;/span> --header &lt;span class="s2">&amp;#34;PRIVATE-TOKEN:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">PRIVATE_TOKEN&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">COUNTBRANCHES&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="nb">echo&lt;/span> &lt;span class="si">${&lt;/span>&lt;span class="nv">LISTMR&lt;/span>&lt;span class="si">}&lt;/span> &lt;span class="p">|&lt;/span> grep -o &lt;span class="s2">&amp;#34;\&amp;#34;source_branch\&amp;#34;:\&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">CI_COMMIT_REF_NAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">\&amp;#34;&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> wc -l&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># No MR found, let&amp;#39;s create a new one&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> &lt;span class="si">${&lt;/span>&lt;span class="nv">COUNTBRANCHES&lt;/span>&lt;span class="si">}&lt;/span> -eq &lt;span class="s2">&amp;#34;0&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> curl -X POST &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">HOST&lt;/span>&lt;span class="si">}${&lt;/span>&lt;span class="nv">CI_PROJECT_ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">/merge_requests&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --header &lt;span class="s2">&amp;#34;PRIVATE-TOKEN:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">PRIVATE_TOKEN&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --header &lt;span class="s2">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --data &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">BODY&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Opened a new merge request: WIP: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">CI_COMMIT_REF_NAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> and assigned to you&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> exit&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;No new merge request opened&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Download &lt;a href="https://gist.github.com/alanhe421/19a9483222bb309a62776901f0493b57" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>What seems like simple configuration actually has some pitfalls&lt;/p>
&lt;ol>
&lt;li>Executing shell scripts requires &lt;code>chmod +x&lt;/code>, otherwise you&amp;rsquo;ll get permission errors&lt;/li>
&lt;li>The reason for using &lt;code>CI_COMMIT_MESSAGE&lt;/code> regex filtering in the &lt;code>except&lt;/code> section is to avoid triggering MRs in cases like when UAT branch receives an SIT MR to UAT - otherwise UAT would MR to SIT again, creating a deadlock.&lt;/li>
&lt;/ol>
&lt;h2 id="automated-merge-request-approval">
&lt;a class="heading-anchor-link" href="#automated-merge-request-approval">Automated Merge Request Approval&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="automated-merge-request-approval"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since we can already use APIs to create merge requests, theoretically we should also be able to automatically approve requests. Investigation revealed that the &lt;a href="https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request" target="_blank" rel="noopener">API&lt;/a> does support this.&lt;/p>
&lt;p>However, it requires meeting two conditions:&lt;/p>
&lt;ol>
&lt;li>&lt;code>Paid version&lt;/code> Bronze / Starter&lt;/li>
&lt;li>Version requirement: Introduced in GitLab Starter 10.6.&lt;/li>
&lt;/ol>
&lt;p>&lt;em>If requirements aren&amp;rsquo;t met, you&amp;rsquo;ll get 404 errors.&lt;/em>&lt;/p>
&lt;p>Since the company uses the community edition, the direct API support route isn&amp;rsquo;t available. For those who meet the requirements, you can simply extend the previous script.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>It may seem like a small step, but it solves labor costs and has real value.&lt;/li>
&lt;li>In principle, it&amp;rsquo;s still git hooks combined with scripted access to GitLab API requests, so even if not using GitLab&amp;rsquo;s CI mechanism, Jenkins could also solve this.&lt;/li>
&lt;/ul>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://about.gitlab.com/blog/2017/09/05/how-to-automatically-create-a-new-mr-on-gitlab-with-gitlab-ci/" target="_blank" rel="noopener">How to automatically create a new MR on GitLab with GitLab CI&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Setting Up the Android Environment for Ionic Development</title><link>https://en.1991421.cn/2020/07/27/ionicandroid/</link><pubDate>Mon, 27 Jul 2020 20:45:06 +0800</pubDate><guid>https://en.1991421.cn/2020/07/27/ionicandroid/</guid><description>&lt;blockquote>
&lt;p>Environment setup isn&amp;rsquo;t necessarily difficult, but it&amp;rsquo;s rarely a perfectly smooth process.&lt;/p>
&lt;/blockquote>
&lt;p>This guide is designed for developers building Android apps with Ionic. Since I work exclusively on macOS, the commands provided are Mac-specific. Windows users will find the general logic similar but should look up the Windows-equivalent CLI commands.&lt;/p>
&lt;h3 id="1-install-java-jdk">
&lt;a class="heading-anchor-link" href="#1-install-java-jdk">1. Install Java JDK&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-install-java-jdk"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Ionic requires the Java Development Kit (JDK). Ensure you have a compatible version installed (check the current Ionic documentation for the recommended version, often JDK 11 or 17).&lt;/p>
&lt;h3 id="2-install-android-studio">
&lt;a class="heading-anchor-link" href="#2-install-android-studio">2. Install Android Studio&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-install-android-studio"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Download and install &lt;a href="https://developer.android.com/studio" target="_blank" rel="noopener">Android Studio&lt;/a>. This provides the necessary SDK managers and build tools.&lt;/p>
&lt;h3 id="3-install-gradle">
&lt;a class="heading-anchor-link" href="#3-install-gradle">3. Install Gradle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-install-gradle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>On macOS, you can easily install Gradle via Homebrew:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">brew install gradle
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-install-android-sdks">
&lt;a class="heading-anchor-link" href="#4-install-android-sdks">4. Install Android SDKs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="4-install-android-sdks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Open Android Studio, go to the &lt;strong>SDK Manager&lt;/strong>, and install the SDK platforms and build tools for your target Android version.&lt;/p>
&lt;h3 id="5-accept-sdk-license-agreements">
&lt;a class="heading-anchor-link" href="#5-accept-sdk-license-agreements">5. Accept SDK License Agreements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="5-accept-sdk-license-agreements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>You must accept the licenses before you can build apps. Run the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">~/Library/Android/sdk/tools/bin/sdkmanager --licenses
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="6-connect-your-physical-device">
&lt;a class="heading-anchor-link" href="#6-connect-your-physical-device">6. Connect Your Physical Device&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="6-connect-your-physical-device"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Enable &lt;strong>Developer Options&lt;/strong> and &lt;strong>USB Debugging&lt;/strong> on your Android phone, then connect it to your Mac. Verify the connection with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">adb devices
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="7-run-on-device-with-hot-reload">
&lt;a class="heading-anchor-link" href="#7-run-on-device-with-hot-reload">7. Run on Device with Hot Reload&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="7-run-on-device-with-hot-reload"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To run your app on the connected device with live-reload enabled, use:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ionic cordova run android -l
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Following these steps will quickly get your Ionic Android development and debugging environment ready.&lt;/p></description></item><item><title>Package.json Usage Tips and Tricks</title><link>https://en.1991421.cn/2020/07/26/package-json/</link><pubDate>Sun, 26 Jul 2020 10:32:44 +0800</pubDate><guid>https://en.1991421.cn/2020/07/26/package-json/</guid><description>&lt;blockquote>
&lt;p>For the package.json file, you shouldn&amp;rsquo;t just know about dependencies/devDependencies. Using package.json effectively can make our applications more robust and development more efficient.&lt;/p>
&lt;/blockquote>
&lt;h2 id="enforcing-yarn-instead-of-npm-for-package-installation">
&lt;a class="heading-anchor-link" href="#enforcing-yarn-instead-of-npm-for-package-installation">Enforcing Yarn Instead of npm for Package Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enforcing-yarn-instead-of-npm-for-package-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There are always people who don&amp;rsquo;t carefully observe the project and use npm for package installation. The best approach isn&amp;rsquo;t documentation, but automatic detection.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Add script
package.json&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;preinstall&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;node ./scripts/checkYarn.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>npm will execute this script before the package installation command starts.&lt;/li>
&lt;li>preinstall, prepublish are some built-in &lt;a href="https://docs.npmjs.com/misc/scripts" target="_blank" rel="noopener">hooks&lt;/a>, though in actual development we&amp;rsquo;ll enrich and extend our own like start, build, etc.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>checkYarn.js&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="sr">/yarn\.js$/&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">npm_execpath&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">warn&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">&amp;#39;\u001b[33mThis repository requires Yarn 1.x for scripts to work properly.\u001b[39m\n&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When using npm install, it will directly report an error.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-26-103535.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="restricting-node-and-yarn-versions">
&lt;a class="heading-anchor-link" href="#restricting-node-and-yarn-versions">Restricting Node and Yarn Versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="restricting-node-and-yarn-versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Inconsistent development environment versions can be problematic. For example, before yarn 1.0, some packages didn&amp;rsquo;t have hash fingerprints. If everyone&amp;rsquo;s yarn packages were inconsistent, once packages were installed, this file would keep changing. So ensuring development environment consistency is extremely important.&lt;/p>
&lt;/blockquote>
&lt;p>package.json&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;engines&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;node&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=10.16.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;yarn&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt;=1.16.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: Version numbers can also be closed intervals like &amp;ldquo;10.15.0-10.16.0&amp;rdquo;.&lt;/p>
&lt;h2 id="license">
&lt;a class="heading-anchor-link" href="#license">License&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="license"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the repository is set to &lt;code>private:true&lt;/code>, the license field doesn&amp;rsquo;t need to be set - npm will ignore this setting. If not private, it needs to be set to any license, like MIT, otherwise you&amp;rsquo;ll get an error during packaging:&lt;/p>
&lt;blockquote>
&lt;p>No license field&lt;/p>
&lt;/blockquote>
&lt;p>Note: Sometimes the missing license prompt doesn&amp;rsquo;t necessarily refer to the package.json file in the project root path, but could be in system user directories, etc. The specific file can be determined based on the error message.&lt;/p>
&lt;h2 id="nested-dependency-package-versions">
&lt;a class="heading-anchor-link" href="#nested-dependency-package-versions">Nested Dependency Package Versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nested-dependency-package-versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Sometimes you want to modify nested dependency package versions. The solution is to add a resolutions field. Configuration example below, using the trim package as an example:&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;resolutions&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;trim&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.0.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If using package-lock.json for version dependency management, you also need to configure a hook. If using yarn.lock, the above configuration is sufficient.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;preinstall&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npx npm-force-resolutions&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="version-numbers-as-branchescommit-idspackage-aliases">
&lt;a class="heading-anchor-link" href="#version-numbers-as-branchescommit-idspackage-aliases">Version Numbers as Branches/Commit IDs/Package Aliases&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="version-numbers-as-branchescommit-idspackage-aliases"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Sometimes the JS module you want to install isn&amp;rsquo;t published as a package. One approach is to use GitHub repo commit/branch as version management. npm supports this.&lt;/p>
&lt;/blockquote>
&lt;p>Example: I want to install code from the ecdsa branch of osstech-jp/forge.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ npm i osstech-jp/forge#ecdsa
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Overall, version numbers can be in the following situations:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;dependencies&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;package1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;git://github.com/username/package.git#commit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;package2&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;git://github.com/username/package.git#branch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;package3&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;npm:package4@1.5.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;package4&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.5.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>The address can be github.com or private repository servers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Services like GitHub verify private keys during cloning - they&amp;rsquo;re not directly open. Configuring URLs like this requires the machine installing packages to have the private key to ensure it can clone the code properly&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Package aliases are mainly used when you don&amp;rsquo;t want to change the package name imported in the program, but want the actual package program to point to another package/address&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Use the above methods as needed.&lt;/p>
&lt;h2 id="custom-field-support">
&lt;a class="heading-anchor-link" href="#custom-field-support">Custom Field Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-field-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>npm package.json supports custom fields. If used in JS, you can require and use them directly.&lt;/p>
&lt;p>For example, the following custom field:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;chainConfig&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sdkVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;v2.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;subscribeMinSdkVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;v2.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A few things to note:&lt;/p>
&lt;ol>
&lt;li>Field names should use a top-level custom field, then nest to define N fields. Avoid prefixes like &lt;code>_&lt;/code> and &lt;code>$&lt;/code>, as these are occupied by the package registration mechanism.&lt;/li>
&lt;li>Top-level field names should avoid conflicts with package mechanism reserved words, like name, scripts, _id, etc.&lt;/li>
&lt;/ol>
&lt;h2 id="version">
&lt;a class="heading-anchor-link" href="#version">version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In JS files, you can use the &lt;code>npm_package_version&lt;/code> environment variable to access this field value&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">npm_package_version&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/10065564/add-custom-metadata-or-config-to-package-json-is-it-valid" target="_blank" rel="noopener">https://stackoverflow.com/questions/10065564/add-custom-metadata-or-config-to-package-json-is-it-valid&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Two‑Way Data Binding</title><link>https://en.1991421.cn/2020/07/12/two-way-data-binding/</link><pubDate>Sun, 12 Jul 2020 22:21:05 +0800</pubDate><guid>https://en.1991421.cn/2020/07/12/two-way-data-binding/</guid><description>&lt;blockquote>
&lt;p>AngularJS popularized two‑way binding (not the first conceptually; ActionScript had analogs). Vue supports it; React does not by default but can emulate it. Using Angular as an example, here’s how it works, with pros/cons.&lt;/p>
&lt;/blockquote>
&lt;p>This is just a starting point for discussion.&lt;/p>
&lt;p>[toc]&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, let&amp;rsquo;s look at how two-way binding is used. In the component below, we declare &lt;code>username&lt;/code> and use &lt;code>[(ngModel)]&lt;/code> for two-way binding, which allows real-time synchronization between the Model (M) and View (V). This is two-way binding.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> form works!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="err">[(&lt;/span>&lt;span class="na">ngModel&lt;/span>&lt;span class="err">)]=&amp;#34;&lt;/span>&lt;span class="na">username&lt;/span>&lt;span class="err">&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> I am {{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> username
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The benefit of this is that the view layer and the form object remain consistent. You directly get the data value of the form object, and type issues are handled in advance, no need for manual operations.&lt;/p>
&lt;h2 id="implementation">
&lt;a class="heading-anchor-link" href="#implementation">Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It&amp;rsquo;s easy to use, now let&amp;rsquo;s see how Angular implements it.&lt;/p>
&lt;h3 id="ngmodel-is-syntax-sugar">
&lt;a class="heading-anchor-link" href="#ngmodel-is-syntax-sugar">&lt;code>[(ngModel)]&lt;/code> is syntax sugar&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ngmodel-is-syntax-sugar"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>You should know that &lt;code>[(ngModel)]&lt;/code> is syntactic sugar. It can be broken down into &lt;code>&amp;lt;input [ngModel]=&amp;quot;username&amp;quot; (ngModelChange)=&amp;quot;username=$event&amp;quot;/&amp;gt;&lt;/code>. &lt;code>ngModel&lt;/code> is a &lt;code>directive&lt;/code>, and &lt;code>ngModelChange&lt;/code> is a method exposed by the directive. So we need to focus on the implementation logic of the ngModel directive.&lt;/p>
&lt;p>data =&amp;gt; view uses []
view =&amp;gt; data uses ()&lt;/p>
&lt;h4 id="ngmodel">[ngModel]&lt;/h4>&lt;blockquote>
&lt;p>When we modify the value of &lt;code>name&lt;/code> in the data, the value in the view changes in real time.&lt;/p>
&lt;/blockquote>
&lt;p>Two questions:&lt;/p>
&lt;ol>
&lt;li>How does Angular know when an object changes? &lt;code>Dirty checking&lt;/code>&lt;/li>
&lt;li>How does Angular update the value to the final native DOM element? &lt;code>Modify the element's value property&lt;/code>&lt;/li>
&lt;/ol>
&lt;h4 id="dirty-checking">Dirty checking&lt;/h4>&lt;p>The full name of dirty checking is &lt;code>dirty data checking&lt;/code>, which refers to data that has changed.&lt;/p>
&lt;p>In Angular, asynchronous operations like Click or XHR automatically trigger change detection, find these changes, and then perform update actions.&lt;/p>
&lt;p>However, I understand that these changes are not immediately updated as soon as they occur. Instead, updates are done in batches after the model stabilizes.&lt;/p>
&lt;p>&lt;em>Note: Angular does not use timed polling for checking.&lt;/em>&lt;/p>
&lt;h4 id="update-the-view">Update the view&lt;/h4>&lt;p>Find the nativeElement through the directive and modify the value property.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// packages/forms/src/directives/ng_model.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">@Optional&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Host&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="nx">parent&lt;/span>: &lt;span class="kt">ControlContainer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">@Optional&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Self&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Inject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">NG_VALIDATORS&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">validators&lt;/span>: &lt;span class="kt">Array&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Validator&lt;/span>&lt;span class="err">|&lt;/span>&lt;span class="na">ValidatorFn&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">@Optional&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Self&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Inject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">NG_ASYNC_VALIDATORS&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">asyncValidators&lt;/span>:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">Array&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">AsyncValidator&lt;/span>&lt;span class="err">|&lt;/span>&lt;span class="na">AsyncValidatorFn&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">@Optional&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Self&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="kd">@Inject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">NG_VALUE_ACCESSOR&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">valueAccessors&lt;/span>: &lt;span class="kt">ControlValueAccessor&lt;/span>&lt;span class="p">[])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">super&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_parent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">parent&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_rawValidators&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">validators&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_rawAsyncValidators&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">asyncValidators&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">valueAccessor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">selectValueAccessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">valueAccessors&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// packages/forms/src/directives/default_value_accessor.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">writeValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">normalizedValue&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">value&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">null&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_renderer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_elementRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nativeElement&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;value&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">normalizedValue&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// packages/platform-browser/src/dom/dom_renderer.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">el&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">NG_DEV_MODE&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">checkNoSyntheticProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;property&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">el&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="ngmodelchange">(ngModelChange)&lt;/h4>&lt;p>Detect changes in the form input value and update the data object.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// packages/forms/src/directives/ng_model.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ngOnChanges&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">changes&lt;/span>: &lt;span class="kt">SimpleChanges&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_checkForErrors&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_registered&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_setUpControl&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;isDisabled&amp;#39;&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nx">changes&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_updateDisabled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">changes&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">isPropertyUpdated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">changes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">viewModel&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">_updateValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">viewModel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">model&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// packages/elements/src/component-factory-strategy.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/** Runs change detection on the component. */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">protected&lt;/span> &lt;span class="nx">detectChanges&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">componentRef&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">callNgOnChanges&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">componentRef&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">componentRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">changeDetectorRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">detectChanges&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we roughly understand the principle, how to implement two-way binding manually without a framework?&lt;/p>
&lt;h2 id="alternatives">
&lt;a class="heading-anchor-link" href="#alternatives">Alternatives&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="alternatives"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="objectdefineproperty">
&lt;a class="heading-anchor-link" href="#objectdefineproperty">Object.defineProperty&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="objectdefineproperty"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Supported in IE9&lt;/li>
&lt;li>Principle behind Vue v2 two-way binding&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">obj&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">defineProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;value&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">get&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Get value&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">val&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">set&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Set value&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="proxy">
&lt;a class="heading-anchor-link" href="#proxy">Proxy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="proxy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Not supported in IE&lt;/li>
&lt;li>Proxy is an ES6 feature&lt;/li>
&lt;li>Principle behind Vue v3 two-way binding&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">obj&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">handler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">get&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">prop&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Get value&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">set&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">prop&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Set value&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">proxy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Proxy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">handler&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sb">```
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">## References
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">- [Angular 2 Change Detection - 2](https://segmentfault.com/a/1190000008754052)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">- [angular dirty checking principle and pseudo-code implementation](https://juejin.im/post/5b193353f265da6e0d7a2dcd)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Closures Explained (Simple Guide)</title><link>https://en.1991421.cn/2020/07/06/understanding-closures/</link><pubDate>Mon, 06 Jul 2020 22:53:06 +0800</pubDate><guid>https://en.1991421.cn/2020/07/06/understanding-closures/</guid><description>&lt;blockquote>
&lt;p>Recently while organizing JavaScript fundamentals, I found the concept of &lt;code>closure&lt;/code> wasn&amp;rsquo;t very clear, so I studied it for a while. As they say, &amp;ldquo;leaving footprints in snow,&amp;rdquo; I&amp;rsquo;m documenting this here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-is-a-closure">
&lt;a class="heading-anchor-link" href="#what-is-a-closure">What is a Closure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-a-closure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;code>A function bundled together with references to its surrounding state (lexical environment)&lt;/code> forms a closure. In other words, closures give you access to an outer function&amp;rsquo;s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.&lt;/p>
&lt;/blockquote>
&lt;p>From MDN&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Function scope, not block scope&lt;/li>
&lt;li>Closures are stateful functions&lt;/li>
&lt;/ol>
&lt;p>Let&amp;rsquo;s start with some examples to see &lt;code>which is a closure&lt;/code>&lt;/p>
&lt;h3 id="example-1">
&lt;a class="heading-anchor-link" href="#example-1">Example 1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">init&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Mozilla&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// name is a local variable created by init
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">displayName&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// displayName() is the inner function
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// uses variable declared in parent function
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">displayName&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">init&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-09-223621.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="example-2">
&lt;a class="heading-anchor-link" href="#example-2">Example 2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">makeFunc&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Mozilla&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">displayName&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">displayName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">myFunc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">makeFunc&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">myFunc&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-09-224053.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="tip">
&lt;a class="heading-anchor-link" href="#tip">TIP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Note that Chrome&amp;rsquo;s debugging state clearly shows &lt;code>which is a closure&lt;/code>. This helps with understanding.&lt;/p>
&lt;p>Through these examples, we can actually say:&lt;/p>
&lt;p>A closure is &lt;code>a function object that can access variables in another function's scope&lt;/code>, and you can also say a closure is a &lt;code>stateful function&lt;/code>.&lt;/p>
&lt;h2 id="using-closures">
&lt;a class="heading-anchor-link" href="#using-closures">Using Closures&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-closures"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we know what closures are, let&amp;rsquo;s see what they can do.&lt;/p>
&lt;ol>
&lt;li>Encapsulating private variables&lt;/li>
&lt;li>Modules&lt;/li>
&lt;li>Using in block scope&lt;/li>
&lt;li>Event listeners&lt;/li>
&lt;/ol>
&lt;p>We may consciously or unconsciously use these patterns, perhaps without realizing it.&lt;/p>
&lt;h2 id="closures-are-a-technique-not-language-specific">
&lt;a class="heading-anchor-link" href="#closures-are-a-technique-not-language-specific">Closures are a Technique, Not Language-Specific&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closures-are-a-technique-not-language-specific"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As mentioned above, closure is a technical approach that&amp;rsquo;s language-independent. So does Java have closures? Yes, it does.&lt;/p>
&lt;h3 id="closures-in-java">
&lt;a class="heading-anchor-link" href="#closures-in-java">Closures in Java&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closures-in-java"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For example, in lambda expressions, we pass in external function variables, forming closures.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">factor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">3&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">numbers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">stream&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">filter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">factor&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">collect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">toList&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Why is this a closure? Note that factor is from the outer function&amp;rsquo;s lexical environment.&lt;/p>
&lt;h2 id="conceptual-confusion---lambda-anonymous-functions-closures">
&lt;a class="heading-anchor-link" href="#conceptual-confusion---lambda-anonymous-functions-closures">Conceptual Confusion - Lambda, Anonymous Functions, Closures&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conceptual-confusion---lambda-anonymous-functions-closures"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Lambda is an anonymous function, just different terminology&lt;/li>
&lt;li>Closures are different from anonymous functions. In the above example, if we remove factor, it&amp;rsquo;s no longer a closure&lt;/li>
&lt;/ol>
&lt;h2 id="practice-problems">
&lt;a class="heading-anchor-link" href="#practice-problems">Practice Problems&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practice-problems"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After understanding closures, let&amp;rsquo;s look at some more complex problems.&lt;/p>
&lt;h3 id="problem-1">
&lt;a class="heading-anchor-link" href="#problem-1">Problem 1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">o&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">o&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fun&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// ③
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">debugger&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">n&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// ④
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// First example
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// returns undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// returns 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// returns 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// returns 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Second example
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined,0,1,2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Third example
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">c&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fun&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined,0,1,1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-10-133720.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="problem-2">
&lt;a class="heading-anchor-link" href="#problem-2">Problem 2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">myObject&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">foo&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;bar&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">func&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">self&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;outer func: this.foo = &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">foo&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// bar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;outer func: self.foo = &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">foo&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// bar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;inner func: this.foo = &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">foo&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;inner func: self.foo = &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">foo&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// bar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">myObject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">func&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-10-180753.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>This problem theoretically tests the &lt;code>this binding&lt;/code> concept. I remember reading a book that had a great line: &amp;ldquo;this always points to the object that calls it.&amp;rdquo; Here, that&amp;rsquo;s myObject.&lt;/li>
&lt;li>func and its context&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>The programming world values simplicity and elegance as beauty. Often, if you find a concept very complex, you might be misunderstanding it.&lt;/code> I deeply agree with this statement. Approaching concepts like closures with this mindset should make them simpler.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures" target="_blank" rel="noopener">MDN-Closures&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_%28%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%29" target="_blank" rel="noopener">Wiki-Closure-Computer Science&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/prettyEcho/deep-js/issues/4" target="_blank" rel="noopener">Closures, Truly Beautiful&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5b783b52f265da433874d88a" target="_blank" rel="noopener">Learning from a JS Closure Interview Question&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97" target="_blank" rel="noopener">Lambda Calculus&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Lazy Loading Principles</title><link>https://en.1991421.cn/2020/07/05/lazy-loading-principle/</link><pubDate>Sun, 05 Jul 2020 19:35:03 +0800</pubDate><guid>https://en.1991421.cn/2020/07/05/lazy-loading-principle/</guid><description>&lt;blockquote>
&lt;p>Recently working on web frontend performance optimization - bundle size reduction.
Today&amp;rsquo;s frameworks have maturely encapsulated functionality, seemingly allowing foolish usage. But knowing only the surface causes problems when any issue arises - you can only wait helplessly for so-called upgrades and seek help in forums, which is pitiful.&lt;/p>
&lt;/blockquote>
&lt;p>Therefore, here&amp;rsquo;s a simple overview of lazy loading.&lt;/p>
&lt;h2 id="implementation-principles">
&lt;a class="heading-anchor-link" href="#implementation-principles">Implementation Principles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-principles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Whether it&amp;rsquo;s React.lazyLoad or Angular&amp;rsquo;s loadChildren, they all use dynamic imports import() in usage&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The reason TS allows this syntax is TypeScript support; the reason JS allows this is webpack support. Of course, after TS compilation, it&amp;rsquo;s still JS that continues webpack bundling.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The final result of import() is:&lt;/p>
&lt;ul>
&lt;li>The imported resource becomes a separate file, so the bundle size is removed from the original main bundle&lt;/li>
&lt;li>The main file executing import gains additional code that, when import is triggered, dynamically creates script tags to request chunk JS resources.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Yes, it&amp;rsquo;s that simple.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-05-203554.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-05-203610.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="worth-noting">
&lt;a class="heading-anchor-link" href="#worth-noting">Worth Noting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="worth-noting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>TypeScript &lt;code>2.4&lt;/code> added dynamic import import() expression support. Current TypeScript version is already &lt;code>3.9&lt;/code>, so you can use it with confidence&lt;/li>
&lt;li>Webpack supports not only &lt;code>import()&lt;/code> for code splitting, but also &lt;code>require.ensure&lt;/code>, which is webpack-specific. Of course, ESModule is the standard, so maintaining one habit is better&lt;/li>
&lt;li>ES import() is currently only a &lt;a href="https://github.com/tc39/proposal-dynamic-import/#import" target="_blank" rel="noopener">proposal&lt;/a> and not yet an official standard, so even when compiling and bundling ES6, import() won&amp;rsquo;t exist&lt;/li>
&lt;/ol>
&lt;h2 id="extension">
&lt;a class="heading-anchor-link" href="#extension">Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above uses script tag creation, but we could also use XHR async requests then eval execution. Below is an example, but note that XHR has cross-origin issues.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">xhrLoad&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">xhr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">XMLHttpRequest&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;GET&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;./xhr-content.js&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">onreadystatechange&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readyState&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">200&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">eval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">xhr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">responseText&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Fancy Console</title><link>https://en.1991421.cn/2020/07/04/console/</link><pubDate>Sat, 04 Jul 2020 15:24:31 +0800</pubDate><guid>https://en.1991421.cn/2020/07/04/console/</guid><description>&lt;blockquote>
&lt;p>Technicians habitually check the webpage console, and we find that some web pages output some excellent console information, mostly recruitment ads in China. But I have always wondered how it is done.&lt;/p>
&lt;/blockquote>
&lt;p>I took time to study it over the weekend and mark it here.&lt;/p>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s show some examples.&lt;/p>
&lt;p>For instance, Baidu&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-04-152507.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Or Zhihu&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-04-152548.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Seeing this raises two questions: How do we achieve the color style and text LOGO such as ASCII art? Surely, it’s not typed out manually.&lt;/p>
&lt;h2 id="how-to">
&lt;a class="heading-anchor-link" href="#how-to">How to&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>The console supports style output; specific configurations can be referred to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Console" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;%cThis will be formatted with large, blue text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;color: blue; font-size: x-large&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>For ASCII art like the &amp;ldquo;hire&amp;rdquo; text, you can use &lt;a href="https://www.kammerl.de/ascii/AsciiSignature.php" target="_blank" rel="noopener">online services&lt;/a>, or for image conversion; you can use &lt;a href="https://github.com/IonicaBizau/image-to-ascii" target="_blank" rel="noopener">this package&lt;/a> to convert images to ASCII.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="my-example">
&lt;a class="heading-anchor-link" href="#my-example">My Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="my-example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After understanding, I quickly implemented it on my blog platform, and the dull console instantly became more fun.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-04-154412.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>HTML Space Display Issues</title><link>https://en.1991421.cn/2020/07/03/nbsp/</link><pubDate>Fri, 03 Jul 2020 13:51:29 +0800</pubDate><guid>https://en.1991421.cn/2020/07/03/nbsp/</guid><description>&lt;blockquote>
&lt;p>In actual web development, I&amp;rsquo;ve seen some people habitually use character entities to control spacing like &lt;code>hello&amp;amp;nbsp;world&lt;/code>. Personally, I don&amp;rsquo;t think this is good practice. Here&amp;rsquo;s a simple explanation.&lt;/p>
&lt;/blockquote>
&lt;h2 id="nbsp">
&lt;a class="heading-anchor-link" href="#nbsp">&amp;amp;nbsp;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nbsp"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Because HTML is a markup language (essentially code), some characters in HTML are reserved characters - spaces, greater than, less than signs, similar to &lt;code>/ .&lt;/code> in regular expressions which are also reserved characters. But sometimes we want to display these characters as text. How do we do that? HTML&amp;rsquo;s solution is to use &lt;code>character entities&lt;/code>, for example, space is &lt;code>&amp;amp;nbsp;&lt;/code>.&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>This is an English space&lt;/li>
&lt;li>&amp;amp;nbsp; stands for &lt;code>non-breaking space&lt;/code>&lt;/li>
&lt;li>Entity names are case-sensitive!&lt;/li>
&lt;/ul>
&lt;h2 id="equivalent-writing">
&lt;a class="heading-anchor-link" href="#equivalent-writing">Equivalent Writing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="equivalent-writing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, in React projects, because JSX is still essentially JavaScript, we can use variables to represent spaces, like &lt;code>hello{' '}world&lt;/code>, which is actually equivalent to &lt;code>hello&amp;amp;nbsp;world&lt;/code>.&lt;/p>
&lt;h2 id="10-spaces">
&lt;a class="heading-anchor-link" href="#10-spaces">10 Spaces?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="10-spaces"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Observant readers will notice that with text spaces, &lt;code>hello{' '}world&lt;/code> still results in only 1 space. Why? Because browsers always truncate the number of spaces in HTML pages. If you want to display 10 spaces, you need to use 10 character entities &lt;code>&amp;amp;nbsp;&lt;/code>.&lt;/p>
&lt;h2 id="disadvantages">
&lt;a class="heading-anchor-link" href="#disadvantages">Disadvantages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="disadvantages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Whether using {&amp;rsquo; &amp;lsquo;} or &lt;code>&amp;amp;nbsp;&lt;/code>, the drawback is that the displayed width of this space varies with different fonts. Another disadvantage is that if you need 10, 20 spaces, it&amp;rsquo;s much less precise and flexible than using CSS control.&lt;/p>
&lt;h2 id="what-should-be-done">
&lt;a class="heading-anchor-link" href="#what-should-be-done">What Should Be Done&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-should-be-done"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In my opinion:&lt;/p>
&lt;ul>
&lt;li>If it&amp;rsquo;s just a simple space and the font variation is acceptable, use text space {&amp;rsquo; &amp;lsquo;} because it&amp;rsquo;s simple enough and part of the code anyway&lt;/li>
&lt;li>If you need many spaces, please consider using CSS solutions instead.&lt;/li>
&lt;/ul>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.w3school.com.cn/html/html_entities.asp" target="_blank" rel="noopener">HTML Character Entities&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Native Browser Support for JS Base64</title><link>https://en.1991421.cn/2020/07/02/js-base64/</link><pubDate>Thu, 02 Jul 2020 14:21:32 +0800</pubDate><guid>https://en.1991421.cn/2020/07/02/js-base64/</guid><description>&lt;blockquote>
&lt;p>While reviewing an MR recently I saw someone call &lt;code>btoa&lt;/code> and was confused—turns out it&amp;rsquo;s the native Base64 encoder. Clearly I had a gap in my JS fundamentals, so I&amp;rsquo;m jotting down a quick recap.&lt;/p>
&lt;/blockquote>
&lt;h2 id="native-base64-encoding-and-decoding-in-browsers">
&lt;a class="heading-anchor-link" href="#native-base64-encoding-and-decoding-in-browsers">Native Base64 encoding and decoding in browsers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="native-base64-encoding-and-decoding-in-browsers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">btoa&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;hello&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// aGVsbG8=
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">atob&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;aGVsbG8=&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// hello
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="how-to-remember-a-to-b">
&lt;a class="heading-anchor-link" href="#how-to-remember-a-to-b">How to remember a to b&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-remember-a-to-b"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&amp;ldquo;a&amp;rdquo; for &amp;ldquo;ASCII&amp;rdquo;&lt;/li>
&lt;li>&amp;ldquo;b&amp;rdquo; for &amp;ldquo;binary&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;p>Note: ASCII only covers English characters. See my earlier &lt;a href="https://1991421.cn/2020/05/09/4d28b2a7/" target="_blank" rel="noopener">post&lt;/a> for more background.&lt;/p>
&lt;h2 id="handling-chinese-characters">
&lt;a class="heading-anchor-link" href="#handling-chinese-characters">Handling Chinese characters&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-chinese-characters"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">btoa&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">encodeURIComponent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;中文&amp;#39;&lt;/span>&lt;span class="p">)));&lt;/span> &lt;span class="c1">// JUU0JUI4JUFEJUU2JTk2JTg3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">decodeURIComponent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">atob&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;JUU0JUI4JUFEJUU2JTk2JTg3&amp;#39;&lt;/span>&lt;span class="p">)));&lt;/span> &lt;span class="c1">// Chinese
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As shown above, strings containing Chinese characters need to be encoded first. Calling &lt;code>btoa&lt;/code> directly throws an error:&lt;/p>
&lt;blockquote>
&lt;p>t2020-07-02-135210.html:14 Uncaught DOMException: Failed to execute &amp;lsquo;btoa&amp;rsquo; on &amp;lsquo;Window&amp;rsquo;: The string to be encoded contains characters outside of the Latin1 range.
at&lt;/p>
&lt;/blockquote>
&lt;p>Why? &lt;code>ASCII doesn't support Chinese...&lt;/code>&lt;/p>
&lt;h2 id="parameter-type-for-btoa">
&lt;a class="heading-anchor-link" href="#parameter-type-for-btoa">Parameter type for btoa&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="parameter-type-for-btoa"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>btoa&lt;/code> expects a string. If you pass a non-string value, JS will coerce it to a string before encoding. For example, &lt;code>window.btoa({ a: '中文' })&lt;/code> is effectively &lt;code>window.btoa('[object Object]')&lt;/code>, so keep that in mind.&lt;/p>
&lt;p>It&amp;rsquo;s a bit safer in TypeScript because the type annotations guide you toward the correct input.&lt;/p>
&lt;h2 id="browser-support">
&lt;a class="heading-anchor-link" href="#browser-support">Browser support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-07-02-144126.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>IE10+ supports it just fine. Need to cover IE9? Sure, brave soul—ship a polyfill.&lt;/p>
&lt;h2 id="say-goodbye-to-js-base64">
&lt;a class="heading-anchor-link" href="#say-goodbye-to-js-base64">Say goodbye to js-base64&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="say-goodbye-to-js-base64"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that browsers support it natively, there&amp;rsquo;s no need to depend on a third-party library. Once I learned about the native APIs, I immediately dropped &lt;a href="https://github.com/dankogai/js-base64" target="_blank" rel="noopener">js-base64&lt;/a>. The direct benefit is a smaller bundle size—it&amp;rsquo;s not a huge change, but still a win.&lt;/p>
&lt;p>&lt;em>js-base64 is 8 KB unminified and 5 KB minified&lt;/em>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A quick note to reinforce the idea and close the knowledge gap.&lt;/p></description></item><item><title>ESLint Shared Configuration</title><link>https://en.1991421.cn/2020/06/27/eslint/</link><pubDate>Sat, 27 Jun 2020 22:05:14 +0800</pubDate><guid>https://en.1991421.cn/2020/06/27/eslint/</guid><description>&lt;blockquote>
&lt;p>Recently, I migrated multiple web projects from TSLint to ESLint. The next issue was sharing ESLint configurations, and naturally, the solution is still &lt;code>npm package management&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;h2 id="package-project-structure">
&lt;a class="heading-anchor-link" href="#package-project-structure">Package Project Structure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-project-structure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">├── CHANGELOG.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── commitlint.config.js
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── index.js // Rule configuration
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── package.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── yarn.lock
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For specific content, &lt;a href="https://github.com/alanhe421/stacker-eslint-config-react" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="key-points">
&lt;a class="heading-anchor-link" href="#key-points">Key Points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Package version management still uses &lt;code>standard-version&lt;/code>&lt;/li>
&lt;li>The &lt;code>files&lt;/code> field in package.json restricts published files to only index.js. Of course, package.json, README.md, and CHANGELOG.md are defaults, so these three can be omitted.&lt;/li>
&lt;li>Shared configuration doesn&amp;rsquo;t just share Rules, but also plugins. This way, actual web projects only need to inherit the package&amp;rsquo;s plugin configuration to get all configuration items completely, so multiple projects&amp;rsquo; ESLint configurations have almost nothing left.&lt;/li>
&lt;li>Here, I published the package to the NPM public repository. Since package names are easily duplicated, I added &lt;code>scope configuration&lt;/code>, which is stacker here.&lt;/li>
&lt;/ol>
&lt;h2 id="publishing">
&lt;a class="heading-anchor-link" href="#publishing">Publishing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="publishing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Login&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm adduser
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## Publish&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm publish --access&lt;span class="o">=&lt;/span>public
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="key-points-1">
&lt;a class="heading-anchor-link" href="#key-points-1">Key Points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Adding access to the publish command is because I added scope, and this defaults to private package publishing, so it needs to be specified explicitly, otherwise it will prompt no permission. Of course, if you need to publish a private package, this isn&amp;rsquo;t necessary.&lt;/li>
&lt;li>If you get a 403 error, one reason is not being logged in, another reason is that the scope is already registered, so you don&amp;rsquo;t have permission.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-28-141032.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>TSLint will be in its final year this year and will exit the historical stage. You can search for the reasons on my blog&lt;/li>
&lt;li>The purpose of ESLint shared configuration is to unify the code style of multiple projects. If sharing isn&amp;rsquo;t implemented, we have to copy and paste to control each project manually, which often results in omissions. Over time, multiple web projects develop many style divergences, and serious cases even have bugs, so it&amp;rsquo;s not a good approach. Now using public packages can quickly unify things. If custom rules are needed, they can also be implemented in the package, and projects only need to upgrade the package to apply them quickly, which is still very convenient.&lt;/li>
&lt;/ol></description></item><item><title>Common Algorithm Problems</title><link>https://en.1991421.cn/2020/06/27/common-algorithm-questions/</link><pubDate>Sat, 27 Jun 2020 19:50:43 +0800</pubDate><guid>https://en.1991421.cn/2020/06/27/common-algorithm-questions/</guid><description>&lt;blockquote>
&lt;p>I practiced several common algorithm problems today, so I&amp;rsquo;m marking them down here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="array-deduplication">
&lt;a class="heading-anchor-link" href="#array-deduplication">Array Deduplication&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="array-deduplication"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="reduce">
&lt;a class="heading-anchor-link" href="#reduce">reduce&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reduce"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">uniqueArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reduce&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">item&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="p">[]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="set-properties">
&lt;a class="heading-anchor-link" href="#set-properties">Set Properties&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="set-properties"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">uniqueArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">Array&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">Set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="for-loop">
&lt;a class="heading-anchor-link" href="#for-loop">for loop&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="for-loop"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">uniqueArr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">item&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="bubble-sort">
&lt;a class="heading-anchor-link" href="#bubble-sort">Bubble Sort&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bubble-sort"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">bubbleSort&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">j&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">j&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">temp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">arr&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">temp&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">arr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="finding-prime-numbers-in-1000-numbers">
&lt;a class="heading-anchor-link" href="#finding-prime-numbers-in-1000-numbers">Finding Prime Numbers in 1000 Numbers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="finding-prime-numbers-in-1000-numbers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>If a number can only be divided by 1 and itself, it&amp;rsquo;s a prime number&lt;/li>
&lt;li>1 is not a prime number&lt;/li>
&lt;/ul>
&lt;p>The first method should have the highest overhead, as each number needs to calculate from 2 to n-1.&lt;/p>
&lt;h3 id="basic-method">
&lt;a class="heading-anchor-link" href="#basic-method">Basic Method&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="basic-method"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">isPrimeNumber&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">num&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">num&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">num&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">num&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">num&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">getPrimeNumbers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">end&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nx">end&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isPrimeNumber&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">time&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;prime numbers - method #1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getPrimeNumbers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">timeEnd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="nx">prime&lt;/span> &lt;span class="nx">numbers&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="nx">method&lt;/span> &lt;span class="err">#&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="trial-division---improved-version">
&lt;a class="heading-anchor-link" href="#trial-division---improved-version">Trial Division - Improved Version&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="trial-division---improved-version"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Note that Math.sqrt square root operation is less efficient than multiplication.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">isPrimeNumber&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">num&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">num&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nx">num&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">num&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="regex-method---clever-approach---not-recommended">
&lt;a class="heading-anchor-link" href="#regex-method---clever-approach---not-recommended">Regex Method - Clever Approach - Not Recommended&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="regex-method---clever-approach---not-recommended"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>High memory consumption, for understanding only.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">isPrimeNumber3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">!&lt;/span>&lt;span class="nb">Boolean&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Array&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/^1?$|^(11+?)\1+$/&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">getPrimeNumbers3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">end&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nx">end&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isPrimeNumber3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="sieve-of-eratosthenes">
&lt;a class="heading-anchor-link" href="#sieve-of-eratosthenes">Sieve of Eratosthenes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sieve-of-eratosthenes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">getPrimeNumbers4&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">end&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">defaultPrimes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Array&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">end&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">fill&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">defaultPrimes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">defaultPrimes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">j&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">defaultPrimes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">j&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">defaultPrimes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="algorithm-performance-comparison">
&lt;a class="heading-anchor-link" href="#algorithm-performance-comparison">Algorithm Performance Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="algorithm-performance-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-27-202503.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="online-code">
&lt;a class="heading-anchor-link" href="#online-code">Online Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="online-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I&amp;rsquo;ve hosted the algorithm on &lt;a href="https://codesandbox.io/s/awesome-sea-83pue?file=/index.html" target="_blank" rel="noopener">codesandbox&lt;/a>, click to view&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I haven&amp;rsquo;t paid much attention to algorithms before, but after some deep reflection, I&amp;rsquo;ve decided to practice deliberately. In fact, algorithms are everywhere - some are abstracted away by various libraries and tools, while others are avoided due to insufficient understanding, which results in increased program overhead. Therefore, I need to strengthen my fundamentals and get started.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/146418699" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/146418699&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cloud.tencent.com/developer/article/1519914" target="_blank" rel="noopener">How to efficiently find prime numbers with algorithms?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://coolshell.cn/articles/2704.html" target="_blank" rel="noopener">Regular expression for checking prime numbers&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Software Metrics - Cyclomatic Complexity</title><link>https://en.1991421.cn/2020/06/27/a02306/</link><pubDate>Sat, 27 Jun 2020 05:57:12 +0800</pubDate><guid>https://en.1991421.cn/2020/06/27/a02306/</guid><description>&lt;blockquote>
&lt;p>Recently while configuring ESLint rules, I revisited the &lt;code>complexity&lt;/code> rule. I previously didn&amp;rsquo;t pay enough attention to this rule and had some blind spots in understanding, so I studied it systematically and documented it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="concept">
&lt;a class="heading-anchor-link" href="#concept">Concept&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concept"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;code>Cyclomatic complexity&lt;/code> is also known as &lt;code>conditional complexity&lt;/code> or &lt;code>circuitous complexity&lt;/code>. It&amp;rsquo;s a software metric proposed by Thomas J. McCabe Sr. in &lt;code>1976&lt;/code> to represent program complexity.&lt;/p>
&lt;/blockquote>
&lt;h2 id="cyclomatic-complexity-calculation">
&lt;a class="heading-anchor-link" href="#cyclomatic-complexity-calculation">Cyclomatic Complexity Calculation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cyclomatic-complexity-calculation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="formula">
&lt;a class="heading-anchor-link" href="#formula">Formula&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="formula"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>M = E − N + 2P
Where:&lt;/p>
&lt;ul>
&lt;li>E is the number of edges in the graph&lt;/li>
&lt;li>N is the number of nodes in the graph&lt;/li>
&lt;li>P is the number of connected components [number of programs; for a single program, P is always 1], as shown in the diagram below it&amp;rsquo;s 1&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="http://static.1991421.cn/2020/2020-05-03-221711.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown in the figure, E = 9, N = 8, P = 1, so the cyclomatic complexity is 9 - 8 + (2*1) = 3&lt;/p>
&lt;p>The above is the standard formula, but a simple approach is actually &lt;code>number of decision nodes plus one&lt;/code>&lt;/p>
&lt;h3 id="node-decision">
&lt;a class="heading-anchor-link" href="#node-decision">Node Decision&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="node-decision"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I personally prefer this direct calculation method as it&amp;rsquo;s simpler.&lt;/p>
&lt;h4 id="decision-nodes">Decision Nodes&lt;/h4>&lt;ul>
&lt;li>if statements&lt;/li>
&lt;li>conditional statements, like ?:&lt;/li>
&lt;li>for statements&lt;/li>
&lt;li>while statements&lt;/li>
&lt;li>try statements&lt;/li>
&lt;li>switch/case statements&lt;/li>
&lt;/ul>
&lt;h4 id="notes">Notes&lt;/h4>&lt;ul>
&lt;li>Start from 1 and go through the program&lt;/li>
&lt;li>Encounter the following keywords or similar ones, add 1: if, while, repeat, for, and, or&lt;/li>
&lt;li>Each case in if-else-if and switch-case statements adds 1&lt;/li>
&lt;/ul>
&lt;h2 id="cyclomatic-complexity-reference-values">
&lt;a class="heading-anchor-link" href="#cyclomatic-complexity-reference-values">Cyclomatic Complexity Reference Values&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cyclomatic-complexity-reference-values"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s a comparison table of complexity values and risk levels. You can see that 20 is already on the edge of maintainability, while 10 is optimal.&lt;/p>
&lt;ul>
&lt;li>01 to 10 – Minimal Risk, Easy to maintain&lt;/li>
&lt;li>11 to 20 – Moderate Risk, Harder to maintain&lt;/li>
&lt;li>21 to 50 – High Risk, Candidates for refactoring/redesign&lt;/li>
&lt;li>Over 50 – Very High Risk&lt;/li>
&lt;/ul>
&lt;h3 id="notes-1">
&lt;a class="heading-anchor-link" href="#notes-1">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>The default value in &lt;a href="https://eslint.org/docs/rules/complexity" target="_blank" rel="noopener">eslint&lt;/a> is 20, while in checkstyle the default value is 10&lt;/li>
&lt;li>Low cyclomatic complexity doesn&amp;rsquo;t necessarily mean good code, but if it&amp;rsquo;s too high, the code is definitely not good and lacks maintainability - this point needs to be clear&lt;/li>
&lt;/ul>
&lt;h2 id="detection-methods">
&lt;a class="heading-anchor-link" href="#detection-methods">Detection Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="detection-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In development, to ensure unified code style, we often use tools like eslint, checkStyle to detect code and report errors for non-compliant standards. These detection tools all support cyclomatic complexity.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>eslint-rules&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>{
&amp;lsquo;complexity&amp;rsquo;: [&amp;rsquo;error&amp;rsquo;, { &amp;lsquo;max&amp;rsquo;: 20 }]
}
```&lt;/p>
&lt;ul>
&lt;li>
&lt;p>checkStyle&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE module PUBLIC &amp;#34;-//Checkstyle//DTD Checkstyle Configuration 1.3//EN&amp;#34; &amp;#34;https://checkstyle.org/dtds/configuration_1_3.dtd&amp;#34;&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;module&lt;/span> &lt;span class="na">name =&lt;/span> &lt;span class="s">&amp;#34;Checker&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;charset&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;module&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;TreeWalker&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;module&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;CyclomaticComplexity&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;max&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;10&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="refactoring-techniques">
&lt;a class="heading-anchor-link" href="#refactoring-techniques">Refactoring Techniques&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="refactoring-techniques"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When facing functions with excessively high complexity values, we need to refactor to solve the problem. How exactly should we handle this?&lt;/p>
&lt;ul>
&lt;li>Extract functions to achieve &lt;code>single function responsibility&lt;/code>, reducing the complexity of individual functions, and naturally lowering complexity values&lt;/li>
&lt;li>When switch and if-else are used extensively, consider polymorphism and map mapping to solve multi-branch problems&lt;/li>
&lt;li>Simplify logical judgments through continuous logical merging and consolidation, sometimes discovering duplicates&lt;/li>
&lt;/ul>
&lt;h2 id="eslint-cyclomatic-complexity-source-code-analysis">
&lt;a class="heading-anchor-link" href="#eslint-cyclomatic-complexity-source-code-analysis">ESLint Cyclomatic Complexity Source Code Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslint-cyclomatic-complexity-source-code-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s understand the implementation principle through ESLint&amp;rsquo;s related rule&lt;/p>
&lt;p>&lt;code>eslint/lib.rules/complexity.js&lt;/code>, link &lt;a href="https://github.com/eslint/eslint/blob/055b80dc89bba2a5ab22f7a27deb40135b5cacfa/lib/rules/complexity.js#L122" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Increase the switch complexity in context
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param {ASTNode} node node to evaluate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @returns {void}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @private
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">increaseSwitchComplexity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Avoiding `default`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">FunctionDeclaration&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">startFunction&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">FunctionExpression&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">startFunction&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ArrowFunctionExpression&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">startFunction&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;FunctionDeclaration:exit&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">endFunction&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;FunctionExpression:exit&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">endFunction&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ArrowFunctionExpression:exit&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">endFunction&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">CatchClause&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ConditionalExpression&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">LogicalExpression&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ForStatement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ForInStatement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ForOfStatement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">IfStatement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">SwitchCase&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseSwitchComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">WhileStatement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">DoWhileStatement&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">increaseComplexity&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The calculation method is to find these decision nodes based on the AST tree and perform corresponding complexity calculations. As shown above, default in switch/case doesn&amp;rsquo;t count.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>I remember reading in a book a memorable line: code readability is for humans. For machines running code, you can theoretically write it any way, but the problem is that humans need to read and understand it, so readability and maintainability become particularly important. So, pay attention to cyclomatic complexity - it&amp;rsquo;s a good metric to urge us to write readable code.&lt;/li>
&lt;li>The significance of cyclomatic complexity is &lt;code>to catch defects before they become defects.&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E5%BE%AA%E7%92%B0%E8%A4%87%E9%9B%9C%E5%BA%A6" target="_blank" rel="noopener">Cyclomatic Complexity WIKI&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.castsoftware.com/glossary/cyclomatic-complexity" target="_blank" rel="noopener">Cyclomatic Complexity&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5da34216e51d4578502c24c5" target="_blank" rel="noopener">Frontend Code Quality - Cyclomatic Complexity Principles and Practice&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://kaelzhang81.github.io/2017/06/18/%E8%AF%A6%E8%A7%A3%E5%9C%88%E5%A4%8D%E6%9D%82%E5%BA%A6/" target="_blank" rel="noopener">Detailed Explanation of Cyclomatic Complexity&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/141734006" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/141734006&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Assertion Practices in JS Development and Debugging</title><link>https://en.1991421.cn/2020/06/25/js-debug-assertion/</link><pubDate>Thu, 25 Jun 2020 00:53:46 +0800</pubDate><guid>https://en.1991421.cn/2020/06/25/js-debug-assertion/</guid><description>&lt;blockquote>
&lt;p>I used to think assertions only served testing. In real projects, assertions appear mostly in unit tests, so I assumed that was the case. That assumption is wrong.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-is-an-assertion">
&lt;a class="heading-anchor-link" href="#what-is-an-assertion">What is an assertion?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-an-assertion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>In programming, an assertion is a logical statement in a program (a condition that is true or false). Its purpose is to mark and validate the developer&amp;rsquo;s expected result. When execution reaches the assertion, it should be true. If not, the program stops and returns an error message.
&amp;hellip;
Assertions help designers develop and understand programs.&lt;/p>
&lt;/blockquote>
&lt;p>Quoted from Wikipedia: &lt;a href="https://zh.wikipedia.org/wiki/%E6%96%B7%E8%A8%80_%28%E7%A8%8B%E5%BC%8F%29" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;p>Assertions are a form of program logic and a design-by-contract idea. Testing uses assertions, but we should not equate testing with assertions.&lt;/p>
&lt;h2 id="assertions-in-unit-tests">
&lt;a class="heading-anchor-link" href="#assertions-in-unit-tests">Assertions in unit tests&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="assertions-in-unit-tests"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You see assertions all the time in tests: expect, assertThat, etc.&lt;/p>
&lt;h3 id="jest">
&lt;a class="heading-anchor-link" href="#jest">Jest&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jest"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should show children elements when permission is true and when is true&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">wrapper&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">shallow&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">LAuthShow&lt;/span> &lt;span class="na">permission&lt;/span> &lt;span class="na">when&lt;/span>&lt;span class="p">&amp;gt;}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">hello&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">LAuthShow&lt;/span>&lt;span class="p">&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">contains&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">hello&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;)).&lt;/span>&lt;span class="nx">toBeTruthy&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="java">
&lt;a class="heading-anchor-link" href="#java">Java&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="java"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">should_response_boolean_when_check_star_given_country&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">country&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;US&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">given&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ppcFeignClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isStarDeal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">country&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="na">willReturn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">isStarDeal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ppcService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isStarDeal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">country&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">assertThat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">isStarDeal&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">isTrue&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="business-code">
&lt;a class="heading-anchor-link" href="#business-code">Business code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="business-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Those are typical test assertions. But you can also add assertions in business code. When unexpected situations happen, assertions can trigger actions (e.g., error logs), making debugging more efficient and complementing exceptions and stack traces.&lt;/p>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">quote&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">buildQuote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">quoteState&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">assert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">Boolean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">quote&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currency&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s1">&amp;#39;currency should not be non-null&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">quoteResponseDTO&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">quotationApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">saveQuote&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">quote&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here I call the backend to store quote; quote.currency should never be null. If it is, I want a warning during dev so I can trace and fix it. Assertions fit this case.&lt;/p>
&lt;p>You can wrap &lt;code>console.assert&lt;/code> to add extra behavior, such as notifications:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">assert&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">condition?&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">message?&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">data&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">[])&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">assert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">condition&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">showNotice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;error&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="strengthen-assertion-handling">
&lt;a class="heading-anchor-link" href="#strengthen-assertion-handling">Strengthen assertion handling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="strengthen-assertion-handling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Do we need assertions in production? I think no. They are for development. So you can guard them by environment and no-op in production.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">assert&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">condition?&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">message?&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">data&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">[])&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;prod&amp;#39;&lt;/span> &lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">assert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">condition&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">showNotice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;error&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="assert-in-spring">
&lt;a class="heading-anchor-link" href="#assert-in-spring">Assert in Spring&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="assert-in-spring"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Spring is a mature Java framework and it also has assertions.&lt;/p>
&lt;p>For example, form validation: this is more elegant than manual checks with exceptions.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">InputStream&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Assert&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">hasText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s">&amp;#34;file input is not a valid file path&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>In short: assertions are a debugging technique, not limited to unit tests.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You might ask: can I solve these without assertions? Yes. Assertions are just a tool. You can use logic checks or exceptions too. But assertions represent a contract: this should never happen. If it does, we should fix the root cause. Exceptions often represent expected errors like 400/404 and are more frequent. They are different.&lt;/p>
&lt;p>For example, you could write an if check to log when currency is empty, but it mixes abnormal logic into normal flow. It works but feels less elegant.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The project I&amp;rsquo;m working on is a data-heavy web platform. Missing key data often causes failures, and it is hard to trace. We used lots of logs and exception handling. In some cases, assertions can improve readability and debuggability.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.runoob.com/w3cnote/c-assert.html" target="_blank" rel="noopener">https://www.runoob.com/w3cnote/c-assert.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E6%96%B7%E8%A8%80_%28%E7%A8%8B%E5%BC%8F%29" target="_blank" rel="noopener">Assertions (programming)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Why Block-Level Elements Cannot Be Contained Within P Tags</title><link>https://en.1991421.cn/2020/06/17/p-tag-block-element/</link><pubDate>Wed, 17 Jun 2020 09:49:44 +0800</pubDate><guid>https://en.1991421.cn/2020/06/17/p-tag-block-element/</guid><description>&lt;blockquote>
&lt;p>Recently during code review, I noticed someone had included div and other block-level tags within P tags. This is incorrect, and this article systematically explains why.&lt;/p>
&lt;/blockquote>
&lt;h2 id="p-tag-positioning">
&lt;a class="heading-anchor-link" href="#p-tag-positioning">P Tag Positioning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="p-tag-positioning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The HTML &lt;p> element (or HTML Paragraph element) represents a paragraph of text. This element typically appears as a block of text separated from adjacent text, either by vertical whitespace or first-line indentation. Additionally, &amp;lt;p&amp;gt; is a block-level element.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Phrasing content defines text and the markup it contains. Some phrasing content constitutes a paragraph.&lt;/p>
&lt;/blockquote>
&lt;p>Quoted from MDN, according to the documentation, P tags allow phrasing content inside them. However, block-level elements are not permitted.&lt;/p>
&lt;h2 id="block-level-elements-within-p-tags">
&lt;a class="heading-anchor-link" href="#block-level-elements-within-p-tags">Block-Level Elements Within P Tags&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="block-level-elements-within-p-tags"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="direct-inclusion-in-html">
&lt;a class="heading-anchor-link" href="#direct-inclusion-in-html">Direct Inclusion in HTML&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="direct-inclusion-in-html"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The browser doesn&amp;rsquo;t report an error, but the code is automatically corrected.&lt;/p>
&lt;p>Source code as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hello world
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After browser parsing, it becomes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hello world
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="notes">Notes&lt;/h4>&lt;ul>
&lt;li>This creates a risk of bugs, for example, if the CSS selector is &lt;code>p&amp;gt;div&lt;/code>, it will fail&lt;/li>
&lt;li>If P tags contain inline elements like span, a, etc., there&amp;rsquo;s no problem&lt;/li>
&lt;/ul>
&lt;h3 id="dom-creation-with-javascript">
&lt;a class="heading-anchor-link" href="#dom-creation-with-javascript">DOM Creation with JavaScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dom-creation-with-javascript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Note that if created with JavaScript, the browser does not &amp;ldquo;correct the problem&amp;rdquo; and will create the DOM elements as-is&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="na">myPara&lt;/span>&lt;span class="err">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#34;myPara&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">innerHTML&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">` &amp;lt;div&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> hello world
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> &amp;lt;/div&amp;gt;`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="react-and-other-frameworks">
&lt;a class="heading-anchor-link" href="#react-and-other-frameworks">React and Other Frameworks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-and-other-frameworks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>SPAs are very popular nowadays, but these frameworks are still JavaScript, so P tags can contain block elements as shown above, but there will be error reporting&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-17-101621.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Although the issue isn&amp;rsquo;t severe, following best practices is important. Standardized HTML benefits SEO and helps developers avoid the problems mentioned above, so it&amp;rsquo;s better to be mindful of this.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/8397852/why-cant-the-p-tag-contain-a-div-tag-inside-it" target="_blank" rel="noopener">Why can&amp;rsquo;t the &amp;lt;p&amp;gt; tag contain a &amp;lt;div&amp;gt; tag inside it?&lt;/a>&lt;/li>
&lt;li>&lt;/li>
&lt;/ul></description></item><item><title>The Deprecated componentWillReceiveProps</title><link>https://en.1991421.cn/2020/06/15/componentwillreceiveprops/</link><pubDate>Mon, 15 Jun 2020 22:45:19 +0800</pubDate><guid>https://en.1991421.cn/2020/06/15/componentwillreceiveprops/</guid><description>&lt;blockquote>
&lt;p>React v16.3+ has marked &lt;code>componentWillReceiveProps&lt;/code> as deprecated, and version 17 will remove it completely. So why is this lifecycle method being deprecated, what are its drawbacks, and how should we handle the same requirements with the new approach? This article will help me organize this function.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-15-231959.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="when-componentwillreceiveprops-triggers">
&lt;a class="heading-anchor-link" href="#when-componentwillreceiveprops-triggers">When componentWillReceiveProps Triggers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-componentwillreceiveprops-triggers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As the name suggests, it triggers when props change, but in reality, it also triggers even when props don&amp;rsquo;t change. When a parent component re-renders, the child component&amp;rsquo;s componentWillReceiveProps will trigger. So, &lt;code>componentWillReceiveProps doesn't only trigger when props actually change.&lt;/code>&lt;/p>
&lt;h2 id="using-componentwillreceiveprops">
&lt;a class="heading-anchor-link" href="#using-componentwillreceiveprops">Using componentWillReceiveProps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-componentwillreceiveprops"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Sometimes our state depends on props, so we might execute setState at this time. But note that setState is an asynchronous operation, and state changes will also trigger a new render.&lt;/li>
&lt;li>If side effects are executed in componentWillReceiveProps, there&amp;rsquo;s a risk of memory overflow. For example, initiating async actions, updating Redux state data, which then triggers component props updates, creating a loop that continuously triggers componentWillReceiveProps.&lt;/li>
&lt;/ol>
&lt;p>Based on these problems, the official team deprecated this function. So how should we handle these requirements now?&lt;/p>
&lt;h2 id="how-we-should-approach-it">
&lt;a class="heading-anchor-link" href="#how-we-should-approach-it">How We Should Approach It&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-we-should-approach-it"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>If state updates depend on props, we can use the new static function &lt;code>getDerivedStateFromProps&lt;/code>&lt;/li>
&lt;li>If it&amp;rsquo;s a side effect, it should be executed in didUpdate, didMount, and user interaction operations&lt;/li>
&lt;/ol>
&lt;h2 id="avoiding-the-use-of-this-deprecated-method-in-projects">
&lt;a class="heading-anchor-link" href="#avoiding-the-use-of-this-deprecated-method-in-projects">Avoiding the Use of This Deprecated Method in Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="avoiding-the-use-of-this-deprecated-method-in-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We can configure appropriate lint rules to ensure this type of code doesn&amp;rsquo;t exist, which also reduces the rewriting costs that future framework upgrades might bring.&lt;/p>
&lt;p>&lt;code>yarn add eslint-plugin-react -d&lt;/code>&lt;/p>
&lt;p>For detailed information, &lt;a href="https://github.com/yannickcr/eslint-plugin-react" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>What we&amp;rsquo;re discussing here is React. Compared to Angular, in Angular when a parent component renders and updates, the child component&amp;rsquo;s change method doesn&amp;rsquo;t re-trigger. So, different frameworks have quite significant differences in these basic hooks.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/" target="_blank" rel="noopener">react-lifecycle-methods-diagram&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://reactjs.org/docs/react-component.html" target="_blank" rel="noopener">React.Component&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>react-intl v3 Upgrade</title><link>https://en.1991421.cn/2020/06/13/react-intl-v3/</link><pubDate>Sat, 13 Jun 2020 16:48:50 +0800</pubDate><guid>https://en.1991421.cn/2020/06/13/react-intl-v3/</guid><description>&lt;blockquote>
&lt;p>Taking advantage of the weekend to upgrade the react-intl library. The current latest version is &lt;code>v4.6.9&lt;/code>, while the project is using version &lt;code>v2.3.18&lt;/code>. I&amp;rsquo;ve decided to upgrade to &lt;code>v3@latest&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;h2 id="benefits-of-the-upgrade">
&lt;a class="heading-anchor-link" href="#benefits-of-the-upgrade">Benefits of the Upgrade&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benefits-of-the-upgrade"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Fixes known bugs - in the previous version, when I passed &lt;code>de-de&lt;/code>, internationalization would fail. The workaround at the time was to always set the localData key to &amp;rsquo;en'&lt;/li>
&lt;li>Reduces bundle size for the localeData portion, as it now uses the browser&amp;rsquo;s built-in Intl APIs&lt;/li>
&lt;li>Migration from &lt;code>withRef&lt;/code> to &lt;code>forwardRef&lt;/code>, which is the current standard way for component reference passing, beneficial for consistency&lt;/li>
&lt;li>Since React, Redux, and other major libraries have already been upgraded, upgrading Intl helps with future iteration upgrades for the overall framework&lt;/li>
&lt;li>Previous internationalization methods like formatMessage returned Node elements (containing Span tags), while v3 returns text. Strictly speaking, rendering will also reduce browser overhead since there are fewer nodes. Of course, for places that only need simple text, this directly satisfies the requirement&lt;/li>
&lt;/ol>
&lt;p>&lt;em>Open source may be free, but version upgrades require caution. However, staying stagnant is detrimental to both evolving web technologies and the product project itself, so progressive upgrades are necessary.&lt;/em>&lt;/p>
&lt;h2 id="upgrade-details">
&lt;a class="heading-anchor-link" href="#upgrade-details">Upgrade Details&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrade-details"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official documentation provides a v2 to v3 upgrade guide, but upgrades are never completely smooth. Here I&amp;rsquo;ll explain the key points one by one.&lt;/p>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>How to upgrade? Look here - I&amp;rsquo;ve listed the main key points. For things like values type files, etc., just directly replace and modify based on the error prompts.&lt;/p>
&lt;h3 id="packagejson">
&lt;a class="heading-anchor-link" href="#packagejson">package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;react-intl&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^3.12.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="note">Note&lt;/h4>&lt;p>v3 comes with built-in TypeScript type definitions, so the previous &lt;code>@types/react-intl&lt;/code> needs to be removed&lt;/p>
&lt;h3 id="tsconfigjson">
&lt;a class="heading-anchor-link" href="#tsconfigjson">tsconfig.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tsconfigjson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;lib&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;es2015&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;es2017&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;dom&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;es2018.intl&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;esnext.intl&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="typingsdts">typings.d.ts&lt;/h4>&lt;p>Because I extend the global window property by assigning the intl instance to it, I need the following definition:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">IntlShape&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-intl&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">declare&lt;/span> &lt;span class="kr">global&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">Window&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">intl&lt;/span>: &lt;span class="kt">IntlShape&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="note-1">Note&lt;/h4>&lt;ul>
&lt;li>If not needed, skip this configuration&lt;/li>
&lt;li>There&amp;rsquo;s a conflict between &lt;code>declare module '*.svg';&lt;/code> and &lt;code>declare global&lt;/code> module declarations - they need to be physically separated into two declaration files. If the global declaration has no imports, then &lt;code>declare global&lt;/code> can be omitted&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-13-171323.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="unit-tests">
&lt;a class="heading-anchor-link" href="#unit-tests">Unit Tests&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="unit-tests"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The intl upgrade also affects unit tests, mainly in the utility functions. Configuration is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">mount&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shallow&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;enzyme&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">React&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createIntl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">IntlProvider&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-intl&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">renderer&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-test-renderer&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">messages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span> &lt;span class="c1">// en.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createIntl&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">locale&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;en&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">messages&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onError&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">nodeWithIntlProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cloneElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">mount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// @ts-ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">wrappingComponent&lt;/span>: &lt;span class="kt">IntlProvider&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wrappingComponentProps&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">locale&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;en&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">defaultLocale&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;en&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">renderWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">IntlProvider&lt;/span> &lt;span class="na">locale&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;en&amp;#39;&lt;/span> &lt;span class="na">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">messages&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">onError&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">}&amp;gt;{&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">IntlProvider&lt;/span>&lt;span class="p">&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">rendererWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">renderer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">IntlProvider&lt;/span> &lt;span class="na">locale&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;en&amp;#39;&lt;/span> &lt;span class="na">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">messages&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">onError&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">}&amp;gt;{&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">IntlProvider&lt;/span>&lt;span class="p">&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="interface-definition-changes">
&lt;a class="heading-anchor-link" href="#interface-definition-changes">Interface Definition Changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="interface-definition-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>InjectedIntlProps =&amp;gt; WrappedComponentProps,
withRef =&amp;gt; forwardRef&lt;/p>
&lt;p>You can do a global replace, but be careful not to make mistakes.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the above configuration, the upgrade can be completed. Of course, considering system stability, I didn&amp;rsquo;t upgrade directly from v2 to v4, but after a quick look, the changes from 3 to 4 are relatively minor. So I&amp;rsquo;ll wait for the system to stabilize for a while before doing the next upgrade.&lt;/p>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://formatjs.io/docs/react-intl/upgrade-guide-3x" target="_blank" rel="noopener">Upgrade Guide (v2 -&amp;gt; v3)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Implementing WebSocket in Projects — Cluster Scenarios</title><link>https://en.1991421.cn/2020/06/03/websocket/</link><pubDate>Wed, 03 Jun 2020 22:55:08 +0800</pubDate><guid>https://en.1991421.cn/2020/06/03/websocket/</guid><description>&lt;blockquote>
&lt;p>Recently, WebSocket was implemented in our site, and immediately we faced communication issues in Web service clusters. In a multi-instance Web service environment, if user A subscribes to a WS channel on server A, but server B initiates a broadcast in the background, user A won&amp;rsquo;t receive the message. So the question is: how to handle broadcasts and send accurate messages to individual users in a cluster scenario?&lt;/p>
&lt;/blockquote>
&lt;h2 id="tech-stack">
&lt;a class="heading-anchor-link" href="#tech-stack">Tech Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tech-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>First, let me introduce our current project&amp;rsquo;s tech stack:&lt;/p>
&lt;ul>
&lt;li>React &lt;code>v16.4.2&lt;/code>&lt;/li>
&lt;li>SpringBoot &lt;code>v2.1.6.RELEASE&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use Redis to share user information and implement message subscription broadcasts.&lt;/p>
&lt;p>How to write the specific code? Without further ado, &lt;a href="https://github.com/alanhe421/spring-websocket-demo" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="general-process">
&lt;a class="heading-anchor-link" href="#general-process">General Process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="general-process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>When a user connects to WS, Redis stores the user information. All cluster instances are connected to Redis, so theoretically they can all access all user information.&lt;/li>
&lt;li>If you want to send a point-to-point message, initiate a Redis broadcast. All instance Redis services will receive the message, find the target UID, and send the message if matched.&lt;/li>
&lt;li>If you want to broadcast, initiate a Redis broadcast. All instance Redis services will receive the message and subsequently send it, so all users will receive it.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-03-232111.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="is-it-necessary-to-add-kafka-or-mq">
&lt;a class="heading-anchor-link" href="#is-it-necessary-to-add-kafka-or-mq">Is it necessary to add Kafka or MQ?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-it-necessary-to-add-kafka-or-mq"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Many WebSocket cluster solutions found online mention adding a message queue. Currently, I don&amp;rsquo;t use it in the project. I personally think Redis is sufficient if the message volume is not large.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you think about it, without a cluster, this problem wouldn&amp;rsquo;t exist.&lt;/p>
&lt;p>So why do we need a cluster? Clustering physically increases the service instances to provide services to more clients. The service content is the same, but it also brings problems, like the one mentioned at the beginning of the article. To face such problems, Redis, as a NoSQL storage service, can be exposed to multiple clients to solve the issue.&lt;/p>
&lt;p>This is the general idea, and I hope it can inspire some thoughts.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.toptal.com/java/stomp-spring-boot-websocket" target="_blank" rel="noopener">Using Spring Boot for WebSocket Implementation with STOMP&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.inslee.cn/2020/05/12/springboot/websocket2.html" target="_blank" rel="noopener">Spring Boot WebSocket clustering example&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.inslee.cn/2020/05/12/springboot/websocket.html" target="_blank" rel="noopener">springboot websocket&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Web Page Font Flickering Issues</title><link>https://en.1991421.cn/2020/05/28/page-font-fout-flash/</link><pubDate>Thu, 28 May 2020 23:21:33 +0800</pubDate><guid>https://en.1991421.cn/2020/05/28/page-font-fout-flash/</guid><description>&lt;h2 id="phenomenon">
&lt;a class="heading-anchor-link" href="#phenomenon">Phenomenon&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="phenomenon"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-28-232331.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="analysis">
&lt;a class="heading-anchor-link" href="#analysis">Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When a web page uses custom fonts, during DOM parsing and rendering, fonts haven&amp;rsquo;t finished loading yet, so the browser immediately displays content using fallback fonts. Once font loading completes, it re-renders and replaces the text with custom fonts.&lt;/p>
&lt;h2 id="solutions">
&lt;a class="heading-anchor-link" href="#solutions">Solutions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we&amp;rsquo;ve identified the problem, let&amp;rsquo;s work on optimization. Improvement approaches include the following aspects:&lt;/p>
&lt;h3 id="css-extraction">
&lt;a class="heading-anchor-link" href="#css-extraction">CSS Extraction&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="css-extraction"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Currently, web styles are bundled into JS and dynamically inserted into the header section each time, which is slower. Additionally, JS changes actually cause fingerprint (content hash) changes, affecting styles and preventing caching, so extraction is beneficial for performance improvement.&lt;/p>
&lt;p>Webpack loader and plugins need the following configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sr">/\.less$/&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">use&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">MiniCssExtractPlugin&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">loader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nx">MiniCssExtractPlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Options similar to the same options in webpackOptions.output
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">filename&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;static/css/[name].[hash].css&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">chunkFilename&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;static/css/[name].[hash].css&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="font-preloading">
&lt;a class="heading-anchor-link" href="#font-preloading">Font Preloading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="font-preloading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="notes">Notes&lt;/h4>&lt;ol>
&lt;li>Preloading prevents fonts from only starting to load during CSS parsing, improving font resource loading priority&lt;/li>
&lt;li>Considering the current web development targets browsers like Chrome, IE11+, Safari, etc., I only load woff2 and woff. Note that in terms of file size: woff2 &amp;lt; woff &amp;lt; ttf&lt;/li>
&lt;li>Since &lt;code>woff2&lt;/code> is not supported in IE, &lt;code>woff&lt;/code> configuration is needed&lt;/li>
&lt;/ol>
&lt;h4 id="configuration">Configuration&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;preload&amp;#34;&lt;/span> &lt;span class="na">as&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;font&amp;#34;&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;font/woff2&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/font/Lato-Regular.woff2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;preload&amp;#34;&lt;/span> &lt;span class="na">as&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;font&amp;#34;&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;font/woff2&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/font/Lato-Bold.woff2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">@font-face {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> font-family: &amp;#39;Lato-Regular&amp;#39;;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> src: url(&amp;#39;../static/fonts/Lato-Regular.woff2&amp;#39;) format(&amp;#39;woff2&amp;#39;), url(&amp;#39;../static/fonts/Lato-Regular.woff&amp;#39;) format(&amp;#39;woff&amp;#39;);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> font-display: auto;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">@font-face {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> font-family: &amp;#39;Lato-Bold&amp;#39;;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> src: url(&amp;#39;../static/fonts/Lato-Bold.woff2&amp;#39;) format(&amp;#39;woff2&amp;#39;), url(&amp;#39;../static/fonts/Lato-Bold.woff&amp;#39;) format(&amp;#39;woff&amp;#39;);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> font-display: auto;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that if font loading is cross-origin, the Link tag needs to be configured with &lt;code>crossorigin&lt;/code>, such as crossorigin=&amp;ldquo;anonymous&amp;rdquo;. Otherwise, even if preloading succeeds, fonts won&amp;rsquo;t actually be applied for security reasons, causing fonts to load twice due to both preloading and CSS font-face declarations.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-05-161050.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="font-file-size">
&lt;a class="heading-anchor-link" href="#font-file-size">Font File Size&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="font-file-size"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The above preloading can separate CSS parsing from font resource loading, but large font files are also problematic. For this reason, configure as above to prioritize downloading woff2, with woff only for IE, and ttf not considered.&lt;/p>
&lt;p>Regarding file size differences between font formats, for example &lt;code>lato-regular&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>woff-&lt;code>309KB&lt;/code>&lt;/li>
&lt;li>woff2-&lt;code>183KB&lt;/code>&lt;/li>
&lt;li>ttf-&lt;code>608KB&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Additionally, there are 2 more approaches to further reduce font file size:&lt;/p>
&lt;ul>
&lt;li>Base64 encode font files, which merges fonts and styles, reducing requests. I didn&amp;rsquo;t adopt this approach, but it&amp;rsquo;s an option&lt;/li>
&lt;li>Use subset embedding, since not all character encodings included in font files are actually used&lt;/li>
&lt;/ul>
&lt;h3 id="font-display-strategy">
&lt;a class="heading-anchor-link" href="#font-display-strategy">Font Display Strategy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="font-display-strategy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If font file loading takes an acceptable time within 3 seconds, when font loading isn&amp;rsquo;t complete, the browser can display content with fallback fonts or hide it first. After 3 seconds, if the font file download is complete, display text with custom fonts; if not, use fallback fonts and swap when font file download completes.&lt;/p>
&lt;p>Font switching creates a flickering effect, so I changed the display strategy to auto, which follows the default strategy - if fonts haven&amp;rsquo;t loaded within 3 seconds, text remains invisible.&lt;/p>
&lt;p>Using swap would immediately display with default fonts while font loads, creating a visible font change effect, which was the original problem.&lt;/p>
&lt;p>However, swap isn&amp;rsquo;t necessarily bad - which strategy to use depends on the situation.&lt;/p>
&lt;p>Auto and block ensure no font change issues occur, but longer text invisibility degrades user experience, so continuous loading experience optimization is needed, such as CDN usage.&lt;/p>
&lt;h2 id="related-terminology">
&lt;a class="heading-anchor-link" href="#related-terminology">Related Terminology&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-terminology"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Web performance is actually an important topic, with terminology such as:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>FOUC (Flash of Unstyled Content)&lt;/p>
&lt;p>During web page rendering, when external styles haven&amp;rsquo;t loaded yet, content is briefly displayed with browser default styles, then returns to normal once external styles load - this page flickering process&lt;/p>
&lt;p>This problem is completely avoidable in today&amp;rsquo;s web if resource loading order is handled properly.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="browser-loading-order-for-html-css-js">
&lt;a class="heading-anchor-link" href="#browser-loading-order-for-html-css-js">Browser Loading Order for HTML, CSS, JS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-loading-order-for-html-css-js"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The direct cause of the initial problem was just needing to adjust font display strategy while improving font download speed, but behind this lies browser resource loading workflow processing, so let me outline this here.&lt;/p>
&lt;/blockquote>
&lt;p>Using an actual SPA index page as an example, the browser loading to parsing and rendering process is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!doctype html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;no-js&amp;#34;&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span> &lt;span class="na">dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;ltr&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">base&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">http-equiv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;X-UA-Compatible&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;IE=edge&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">http-equiv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Cache-Control&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;no-cache&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">http-equiv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Pragma&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;no-cache&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">http-equiv&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Expires&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;0&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>loading&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;description&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Description for quotation&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;google&amp;#34;&lt;/span> &lt;span class="na">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;notranslate&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;viewport&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;width=device-width, initial-scale=1, shrink-to-fit=no&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;theme-color&amp;#34;&lt;/span> &lt;span class="na">content&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;#000000&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;preload&amp;#34;&lt;/span> &lt;span class="na">as&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;font&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/font/Lato-Regular.ttf&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;preload&amp;#34;&lt;/span> &lt;span class="na">as&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;font&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/font/Lato-Bold.ttf&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;shortcut icon&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;favicon.ico&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;manifest&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;manifest.webapp&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/css/antd.min.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">style&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nc">browser-upgrade&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">margin&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.2&lt;/span>&lt;span class="kt">em&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ccc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.2&lt;/span>&lt;span class="kt">em&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/css/vendors.2a1bd1c1e210b6a1697e.css&amp;#34;&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/css/main.2a1bd1c1e210b6a1697e.css&amp;#34;&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;root&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/javascript&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;app/vendors.2a1bd1c1e210b6a1697e.chunk.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/javascript&amp;#34;&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;app/main.2a1bd1c1e210b6a1697e.bundle.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/js/stomp.min.js&amp;#34;&lt;/span> &lt;span class="na">defer&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;static/js/sockjs-1.0.0.min.js&amp;#34;&lt;/span> &lt;span class="na">defer&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">body&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-29-163213.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Please point out any errors&lt;/em>&lt;/p>
&lt;h3 id="additional-notes">
&lt;a class="heading-anchor-link" href="#additional-notes">Additional Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="additional-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Browsers have two ways to handle CSS blocking rendering: either wait for CSS parsing to complete before rendering together (Chrome does this, but causes white screen), or render unstyled DOM first then render again once CSS parsing is complete (Firefox does this, but causes unstyled content flashing).&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Browser-related knowledge points are quite fragmented and complex&amp;hellip; Let&amp;rsquo;s work together on this.&lt;/p>
&lt;h2 id="reference-documents">
&lt;a class="heading-anchor-link" href="#reference-documents">Reference Documents&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documents"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.im/post/5b84a885e51d4538d041a674" target="_blank" rel="noopener">Two standards that help prevent font flicker&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://xiaoiver.github.io/coding/2018/03/22/%E5%AD%97%E4%BD%93%E5%8A%A0%E8%BD%BD%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5.html" target="_blank" rel="noopener">Best practices for font loading&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/90172207" target="_blank" rel="noopener">Web performance optimization: FOUC&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization?hl=zh-cn" target="_blank" rel="noopener">Web font optimization&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.innoq.com/en/blog/loading-javascript/" target="_blank" rel="noopener">How Browsers Load and Process JavaScript&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>No Nested Ternary</title><link>https://en.1991421.cn/2020/05/23/no-nested-ternary/</link><pubDate>Sat, 23 May 2020 21:04:11 +0800</pubDate><guid>https://en.1991421.cn/2020/05/23/no-nested-ternary/</guid><description>&lt;blockquote>
&lt;p>Ternary expressions are quite concise when used for simple conditional value assignment, but when encountering nested logic, readability becomes very poor. In actual projects, I require the team to =&amp;gt; disable them.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">foo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">bar&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">baz&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">qux&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">quxx&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">bing&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="nx">bam&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="no-nested-ternary">
&lt;a class="heading-anchor-link" href="#no-nested-ternary">no-nested-ternary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="no-nested-ternary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To constrain the existence of such code, we add &lt;code>no-nested-ternary&lt;/code> in eslint.&lt;/p>
&lt;p>Rule introduction &lt;a href="https://eslint.org/docs/rules/no-nested-ternary" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;p>Of course, for readability, we can add parentheses to improve it, but practice shows this doesn&amp;rsquo;t solve the problem. So, it&amp;rsquo;s not recommended.&lt;/p>
&lt;p>So how should we write equivalent replacements for this type of logic?&lt;/p>
&lt;h2 id="equivalent-replacement-methods">
&lt;a class="heading-anchor-link" href="#equivalent-replacement-methods">Equivalent Replacement Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="equivalent-replacement-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>if else&lt;/li>
&lt;li>switch&lt;/li>
&lt;li>Strategy pattern, building strategy objects&lt;/li>
&lt;/ol>
&lt;p>The above methods can replace our original nested ternary expression writing.&lt;/p></description></item><item><title>Reading the Alibaba Java Development Manual</title><link>https://en.1991421.cn/2020/05/18/38259457/</link><pubDate>Mon, 18 May 2020 04:19:03 +0800</pubDate><guid>https://en.1991421.cn/2020/05/18/38259457/</guid><description>&lt;blockquote>
&lt;p>Alibaba recently released the &lt;strong>Java Development Manual (Taishan Edition)&lt;/strong>. I spent some time with it and jotted down a few reflections.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-18-052109.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="why-conventions-matter">
&lt;a class="heading-anchor-link" href="#why-conventions-matter">Why conventions matter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-conventions-matter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Some developers dislike rules, but imagine ten teammates each following different styles. With overlapping code, maintenance becomes a nightmare. Consistency enables collaboration.&lt;/li>
&lt;li>Standards lower the barrier to contribute and make code faster to read. When everyone follows the same patterns, bug rates drop.&lt;/li>
&lt;li>Even solo developers benefit from personal conventions. Standardization boosts efficiency over time.&lt;/li>
&lt;li>These guidelines are distilled from countless bugs and lessons learned. They may not be perfect, but they’re worth studying.&lt;/li>
&lt;/ul>
&lt;p>Skipping conventions might work today, but problems accumulate and eventually hurt everyone involved.&lt;/p>
&lt;h2 id="tooling-lint">
&lt;a class="heading-anchor-link" href="#tooling-lint">Tooling: lint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tooling-lint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Teams often fluctuate and skill levels vary, so we rely on tooling—Checkstyle, the Alibaba IDE plugin, ESLint, etc.—to enforce baseline conventions.&lt;/li>
&lt;li>Linting is only one layer; merge requests and code reviews reinforce the same goals: uniformity and quality.&lt;/li>
&lt;/ul>
&lt;h2 id="rules-worth-revisiting">
&lt;a class="heading-anchor-link" href="#rules-worth-revisiting">Rules worth revisiting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rules-worth-revisiting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;code>FIXME&lt;/code> marks an issue that needs immediate attention; &lt;code>TODO&lt;/code> marks something to handle later. IDEs like IntelliJ/WebStorm &lt;a href="https://www.jetbrains.com/help/webstorm/using-todo.html" target="_blank" rel="noopener">recognize both&lt;/a>.&lt;/li>
&lt;li>Avoid magic values:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;foo&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;bar&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;baz&amp;#39;&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">dataLast&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// ✗ magic number
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>Without reading the whole block you can’t tell what &lt;code>2&lt;/code> means. Introduce a named constant instead.&lt;/li>
&lt;li>Cap a single method at ~60 lines. Separate the “highlight” logic from supporting details—extract helper methods for the green leaves so the red flower (the core logic) stands out.&lt;/li>
&lt;li>Be careful with commented-out code. Either explain why it’s temporary or delete it. Version control lets you recover old logic; if you must comment something temporarily, leave a reason.&lt;/li>
&lt;li>Avoid monolithic constants or util classes. Organize by business meaning so others can locate values quickly.&lt;/li>
&lt;/ol>
&lt;p>Plenty more rules exist—experience will tell you which ones matter most in your projects.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Software evolves constantly; methodologies endure. Enforcing conventions creates predictability and helps teams adapt. Put the manual into practice—it pays off.&lt;/p></description></item><item><title>Architecture Decision Records — ADR</title><link>https://en.1991421.cn/2020/05/16/architecture-decision-record/</link><pubDate>Sat, 16 May 2020 20:03:52 +0800</pubDate><guid>https://en.1991421.cn/2020/05/16/architecture-decision-record/</guid><description>&lt;blockquote>
&lt;p>During a year-end review, we discussed how projects constantly face new situations. The architecture evolves, and so do the people building it. We need systematic reflection, adjustment, and knowledge sharing to keep everyone aligned.&lt;/p>
&lt;p>A teammate suggested Architecture Decision Records (ADRs). I’d skimmed the concept before and then forgotten it—time to close the loop.&lt;/p>
&lt;/blockquote>
&lt;h2 id="adr">
&lt;a class="heading-anchor-link" href="#adr">ADR&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adr"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>ADR stands for Architecture Decision Record. Projects are living systems, so we need a structured way to document their major decisions. The team also needs an easy way to consume that information, and documentation fits naturally.&lt;/p>
&lt;h2 id="current-practice">
&lt;a class="heading-anchor-link" href="#current-practice">Current Practice&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-practice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Blog posts—handy for personal summaries&lt;/li>
&lt;li>Internal documentation—Confluence, etc.&lt;/li>
&lt;li>Tribal knowledge—in someone’s head&lt;/li>
&lt;/ol>
&lt;p>That’s clearly not systematic. ADRs fill the gap.&lt;/p>
&lt;h2 id="adr-template">
&lt;a class="heading-anchor-link" href="#adr-template">ADR Template&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adr-template"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The community shares a commonly used template. Treat it as a starting point, not orthodoxy—adapt it to deliver value.&lt;/p>
&lt;ul>
&lt;li>Template: &lt;a href="https://github.com/pmerson/ADR-template/blob/master/ADR-template_zh-CN.md" target="_blank" rel="noopener">ADR template (zh-CN)&lt;/a>&lt;/li>
&lt;li>Talk: &lt;a href="https://www.youtube.com/watch?time_continue=19&amp;amp;v=41NVge3_cYo&amp;amp;feature=emb_logo" target="_blank" rel="noopener">SATURN 2017 — Architecture Decision Records in Action&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>CL means changelist. Version control captures code, but ADR decisions don’t map one-to-one with commits, and some decisions may be discouraged or deprecated later. CLs alone aren’t enough.&lt;/li>
&lt;li>Architecture changes the codebase. Keeping ADR Markdown files alongside the repository provides context and history together.&lt;/li>
&lt;li>ADRs are just one systematic answer to a software-engineering need—use them pragmatically.&lt;/li>
&lt;/ol></description></item><item><title>TypeScript Decorator Practice</title><link>https://en.1991421.cn/2020/05/16/typescript/</link><pubDate>Sat, 16 May 2020 16:34:30 +0800</pubDate><guid>https://en.1991421.cn/2020/05/16/typescript/</guid><description>&lt;blockquote>
&lt;p>Decorators are still an &lt;a href="https://github.com/tc39/proposal-decorators" target="_blank" rel="noopener">ES stage-2 proposal&lt;/a>. In TypeScript/Babel projects we can opt in to experimental features, and frameworks like Angular make heavy use of them. I’d used decorators in Angular, but hadn’t explored them deeply—time to change that.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-16-172733.jpeg"
alt="decorator illustration"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="whats-a-decorator">
&lt;a class="heading-anchor-link" href="#whats-a-decorator">What’s a Decorator?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="whats-a-decorator"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Decorators “wrap” classes/members to add behavior without changing the original interface. Consumers shouldn’t notice a difference.&lt;/p>
&lt;/blockquote>
&lt;p>Types:&lt;/p>
&lt;ul>
&lt;li>Class decorators&lt;/li>
&lt;li>Method decorators&lt;/li>
&lt;li>Accessor decorators&lt;/li>
&lt;li>Property decorators&lt;/li>
&lt;li>Parameter decorators&lt;/li>
&lt;li>Metadata decorators&lt;/li>
&lt;/ul>
&lt;p>Key points:&lt;/p>
&lt;ol>
&lt;li>A decorator is just a function.&lt;/li>
&lt;li>In TS they only apply to classes and class elements—not enums, constants, or standalone functions.&lt;/li>
&lt;/ol>
&lt;h3 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;strong>Property decorator&lt;/strong> — tweak a property value:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">modifyProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">target&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">propertyKey&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">target&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">propertyKey&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">random&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Class decorator&lt;/strong> — modify every property:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">modifyProps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">prefix&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">constructor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">keys&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">constructor&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">constructor&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">prefix&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="enabling-decorators">
&lt;a class="heading-anchor-link" href="#enabling-decorators">Enabling Decorators&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enabling-decorators"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using TypeScript + Babel:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">yarn add @babel/plugin-proposal-decorators -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>tsconfig.json&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;experimentalDecorators&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;emitDecoratorMetadata&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>(Enable &lt;code>emitDecoratorMetadata&lt;/code> if you rely on &lt;code>reflect-metadata&lt;/code>.)&lt;/em>&lt;/p>
&lt;p>&lt;code>babel.config.js&lt;/code> (put the plugin first):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;@babel/plugin-proposal-decorators&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">legacy&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Babel transpiles decorators to regular functions, so browser compatibility doesn’t worsen.&lt;/p>
&lt;h2 id="practical-example">
&lt;a class="heading-anchor-link" href="#practical-example">Practical Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practical-example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Our Redux action types were defined like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createActionPrefix&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;redux-actions-helper&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">prefixCreator&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createActionPrefix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;USER&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">UserActionTypes&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">static&lt;/span> &lt;span class="nx">INIT_USER&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">prefixCreator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;INIT_USER&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">static&lt;/span> &lt;span class="nx">SET_BUSINESS_UNIT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">prefixCreator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SET_BUSINESS_UNIT&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Refactor with a class decorator:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createActionPrefix&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;redux-actions-helper&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@actionTypes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;USER&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">UserActionTypes&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">static&lt;/span> &lt;span class="nx">INIT_USER&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">static&lt;/span> &lt;span class="nx">SET_BUSINESS_UNIT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">actionTypes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">prefix&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">constructor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">factory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createActionPrefix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">prefix&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">keys&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">constructor&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">factory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Cleaner and non-invasive.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Decorators are just functions, but they encourage a design pattern: extend behavior without cluttering the original class. Use them thoughtfully.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html" target="_blank" rel="noopener">TypeScript Decorators&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/22277764" target="_blank" rel="noopener">Blog: decorators overview (Chinese)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Google Engineering Practices — CR and MR</title><link>https://en.1991421.cn/2020/05/15/crmr/</link><pubDate>Fri, 15 May 2020 11:50:21 +0800</pubDate><guid>https://en.1991421.cn/2020/05/15/crmr/</guid><description>&lt;blockquote>
&lt;p>I spent about an hour reading Google’s engineering practices (currently focusing on CR and MR). It resonated strongly, so here are my takeaways.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-15-115215.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Official docs: &lt;a href="https://github.com/google/eng-practices" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;h2 id="standardizing-cls">
&lt;a class="heading-anchor-link" href="#standardizing-cls">Standardizing CLs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="standardizing-cls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>CL means changelist&lt;/li>
&lt;li>The subject line should be imperative — a concise command that expresses what this change does. Put context and tracking IDs in the body.&lt;/li>
&lt;li>Don’t batch a pile of unrelated changes into one MR. Large diffs increase review, merge, and rollback risk. Train yourself to split changes and keep each CL as small as practical.&lt;/li>
&lt;/ol>
&lt;h2 id="standardizing-mrs">
&lt;a class="heading-anchor-link" href="#standardizing-mrs">Standardizing MRs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="standardizing-mrs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>For urgent MRs, relax rules appropriately — some changes, if not merged quickly, can cause real business loss.&lt;/li>
&lt;li>Keep MR comments professional and issue‑focused. The goal is to discuss problems and help developers grow.&lt;/li>
&lt;li>For stylistic nits without a team rule, accept the existing style.&lt;/li>
&lt;li>Acknowledge and praise good work.&lt;/li>
&lt;li>LGTM means “looks good to me”.&lt;/li>
&lt;li>When resolving conflicts, involve the developer when needed to avoid lost or incorrect code.&lt;/li>
&lt;/ol>
&lt;h2 id="standardizing-crs">
&lt;a class="heading-anchor-link" href="#standardizing-crs">Standardizing CRs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="standardizing-crs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Speed reduces frustration — don’t let reviews drag on.&lt;/li>
&lt;li>Issues found in CRs should be fixed by the author, especially serious ones. “We’ll fix it later” is a common path to system decay.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Google’s write‑up is strong because the issues and explanations map directly to the pain points I’ve hit while pushing CR/MR discipline. It provides a solid theoretical foundation.&lt;/li>
&lt;li>Great teams don’t face unique problems; they apply more scientific solutions to common ones. The smart approach is to see past symptoms to root causes and fix them properly.&lt;/li>
&lt;/ol></description></item><item><title>Site Security - CSP</title><link>https://en.1991421.cn/2020/05/11/site-security-csp/</link><pubDate>Mon, 11 May 2020 20:31:06 +0800</pubDate><guid>https://en.1991421.cn/2020/05/11/site-security-csp/</guid><description>&lt;blockquote>
&lt;p>Recently, the project was undergoing security scanning, and the security team reported an issue - CSP was not satisfied. As shown in the image below, the error message was already clear, but since I hadn&amp;rsquo;t studied this before, I began investigating and learning.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-11-203614.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="content-security-policy-csp">
&lt;a class="heading-anchor-link" href="#content-security-policy-csp">Content Security Policy (CSP)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="content-security-policy-csp"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The essence of CSP is a whitelist system. Developers explicitly tell the client which external resources can be loaded and executed, equivalent to providing a whitelist. Its implementation and execution are all handled by the browser; developers only need to provide the configuration.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-11-205930.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="configuration-methods">
&lt;a class="heading-anchor-link" href="#configuration-methods">Configuration Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Response header configuration &lt;code>(the method my current project chose)&lt;/code>&lt;/li>
&lt;li>HTML meta tag configuration&lt;/li>
&lt;/ol>
&lt;h3 id="configuration-control-content">
&lt;a class="heading-anchor-link" href="#configuration-control-content">Configuration Control Content&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration-control-content"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>script-src&lt;/li>
&lt;li>style-src&lt;/li>
&lt;li>img-src&lt;/li>
&lt;li>frame-src&lt;/li>
&lt;li>default-src&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>As mentioned earlier, configuration policies can strictly control our resource types, source addresses, and protocol types.&lt;/p>
&lt;h3 id="posting-my-current-configuration">
&lt;a class="heading-anchor-link" href="#posting-my-current-configuration">Posting My Current Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="posting-my-current-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>My current configuration is still relatively loose, only controlling that resource sources must be from myself&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">content-security-policy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;default-src &amp;#39;self&amp;#39; wss: &amp;#39;unsafe-inline&amp;#39; file: data: blob: https://*;&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-06-09-203930.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="content-format">
&lt;a class="heading-anchor-link" href="#content-format">Content Format&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="content-format"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>How to configure? Just know the format and values.&lt;/p>
&lt;ol>
&lt;li>script-src, default-src we can call directives, so we configure one or more directives. As above, I only configured &lt;code>default-src&lt;/code>, &lt;code>directives are separated by semicolons&lt;/code>&lt;/li>
&lt;li>Multiple sources in a single directive, sources can be domains, IPs, protocols. Note that protocols need to include the colon&lt;/li>
&lt;li>unsafe-inline, unsafe-eval, none must have single quotes.&lt;/li>
&lt;/ol>
&lt;p>This is why various writing styles exist in directive configuration - semicolons, single quotes, colons, spaces.&lt;/p>
&lt;h3 id="common-security-strategies">
&lt;a class="heading-anchor-link" href="#common-security-strategies">Common Security Strategies&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-security-strategies"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>HTTPS - essentially today many websites are &lt;code>HTTPS&lt;/code>. If not, for example, Chrome will indicate the site is insecure. Why is HTTPS secure? Because it encrypts all content.&lt;/li>
&lt;li>For high-traffic sites, to improve access speed, we generally CDN-ize static resources like images. But what if these CDN resources get used by others? That would be traffic theft. So there are security strategies, like judging based on &lt;code>referer&lt;/code> source address. If the CDN resource wasn&amp;rsquo;t requested by site A, then directly intercept.&lt;/li>
&lt;li>For example, sessionId, tokens, set as &lt;code>HttpOnly&lt;/code>. Because this information itself doesn&amp;rsquo;t represent anything - we often use tokens to exchange for user information, so the consumer is the server side. To ensure they won&amp;rsquo;t be tampered with by scripts, etc., they need to be set. When JavaScript gets cookies, it will skip cookie records with HttpOnly = true.&lt;/li>
&lt;/ol>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP" target="_blank" rel="noopener">Content Security Policy (CSP)&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.ruanyifeng.com/blog/2016/09/csp.html" target="_blank" rel="noopener">Content Security Policy Beginner Tutorial&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Migrating from TSLint to ESLint</title><link>https://en.1991421.cn/2020/05/02/tslinteslint/</link><pubDate>Sat, 02 May 2020 23:31:42 +0800</pubDate><guid>https://en.1991421.cn/2020/05/02/tslinteslint/</guid><description>&lt;blockquote>
&lt;p>TSLint is deprecated. According to the roadmap, it’s shutting down by the end of 2020. Consolidating on ESLint lets the JS/TS community share tooling, and teams only need to remember one ruleset. I migrated over a weekend; here’s the process.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-03-112511.jpeg"
alt="lint illustration"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="tslint-roadmap-highlights">
&lt;a class="heading-anchor-link" href="#tslint-roadmap-highlights">TSLint Roadmap Highlights&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tslint-roadmap-highlights"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If your project is still active, migration is &lt;em>mandatory&lt;/em>. Key dates:&lt;/p>
&lt;ul>
&lt;li>2019-08-01: No new core rules (bug fixes only).&lt;/li>
&lt;li>2019-11-01: No new features (except migration helpers).&lt;/li>
&lt;li>2020-01-01: Only security fixes / breaking TS changes.&lt;/li>
&lt;li>2020-12-01: Stop accepting PRs entirely.&lt;/li>
&lt;/ul>
&lt;p>Details: &lt;a href="https://github.com/palantir/tslint/issues/4534" target="_blank" rel="noopener">https://github.com/palantir/tslint/issues/4534&lt;/a>&lt;/p>
&lt;h2 id="automated-migration-tslint-to-eslint-config">
&lt;a class="heading-anchor-link" href="#automated-migration-tslint-to-eslint-config">Automated Migration: &lt;code>tslint-to-eslint-config&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="automated-migration-tslint-to-eslint-config"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The community provides a migration tool mapping TSLint rules to ESLint equivalents. Use it if possible. When I tried it (pre-1.0) it was still rough, so I migrated manually—which also forced me to learn the rules.&lt;/p>
&lt;p>&lt;em>Note:&lt;/em> Some TSLint rules have no ESLint counterpart. Read the console output carefully.&lt;/p>
&lt;h2 id="manual-migration">
&lt;a class="heading-anchor-link" href="#manual-migration">Manual Migration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="manual-migration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="setup-packagejson">
&lt;a class="heading-anchor-link" href="#setup-packagejson">Setup &lt;code>package.json&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup-packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@typescript-eslint/eslint-plugin&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^2.30.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@typescript-eslint/eslint-plugin-tslint&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^2.30.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@typescript-eslint/parser&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^2.30.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^6.8.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint-config-prettier&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^6.11.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint-loader&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^4.0.2&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint-plugin-import&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^2.20.2&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint-plugin-jsdoc&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^24.0.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;eslint-plugin-prefer-arrow&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^1.2.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;lint&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;eslint src/main/webapp/**/*.{ts,tsx}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="eslintrcjs">
&lt;a class="heading-anchor-link" href="#eslintrcjs">&lt;code>.eslintrc.js&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eslintrcjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">root&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parser&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;@typescript-eslint/parser&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;@typescript-eslint&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">extends&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;eslint:recommended&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;plugin:@typescript-eslint/eslint-recommended&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;plugin:@typescript-eslint/recommended&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rules&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="webpackconfig">
&lt;a class="heading-anchor-link" href="#webpackconfig">&lt;code>webpack.config&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpackconfig"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sr">/\.tsx?$/&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">enforce&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;pre&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;eslint-loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">exclude&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">utils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">root&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;node_modules&amp;#39;&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="update-disable-comments">
&lt;a class="heading-anchor-link" href="#update-disable-comments">Update Disable Comments&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="update-disable-comments"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Replace TSLint-specific disable comments with ESLint equivalents or remove them if unnecessary.&lt;/p>
&lt;h2 id="handy-tips">
&lt;a class="heading-anchor-link" href="#handy-tips">Handy Tips&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handy-tips"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="disable-examples">
&lt;a class="heading-anchor-link" href="#disable-examples">Disable Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="disable-examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;code>/* eslint-disable no-var */&lt;/code> — disable a rule for the entire file.&lt;/li>
&lt;li>&lt;code>// eslint-disable-next-line complexity&lt;/code> — disable a rule for the next line.&lt;/li>
&lt;li>&lt;code>// eslint-disable-line @typescript-eslint/naming-convention&lt;/code> — disable for the current line.&lt;/li>
&lt;/ol>
&lt;p>Always prefer fixing the issue when possible.&lt;/p>
&lt;h3 id="only-show-errors">
&lt;a class="heading-anchor-link" href="#only-show-errors">Only Show Errors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="only-show-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use &lt;code>--quiet&lt;/code> to suppress warnings:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ eslint src/main/webapp/**/*.&lt;span class="o">{&lt;/span>ts,tsx&lt;span class="o">}&lt;/span> --quiet
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Don’t chase a huge list of custom rules—defaults exist for a reason. Adopt widely-used recommendations.&lt;/li>
&lt;li>Rules enforce consistency and prevent low-level bugs, especially in teams of varying experience.&lt;/li>
&lt;li>The ecosystem is converging: ESLint replaces TSLint, Babel transpiles TS, etc. Embrace the consolidation.&lt;/li>
&lt;/ol></description></item><item><title>Migrating from ts-loader to babel-loader</title><link>https://en.1991421.cn/2020/05/02/ts-loaderbabel-loader/</link><pubDate>Sat, 02 May 2020 15:45:42 +0800</pubDate><guid>https://en.1991421.cn/2020/05/02/ts-loaderbabel-loader/</guid><description>&lt;h2 id="background">
&lt;a class="heading-anchor-link" href="#background">Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Babel 7 added TypeScript transpilation, so we’re no longer limited to &lt;code>ts-loader&lt;/code> or &lt;code>awesome-typescript-loader&lt;/code>.&lt;/li>
&lt;li>I wanted better error readability for redux-saga and found &lt;code>babel-plugin-redux-saga&lt;/code>, which is, naturally, a Babel plugin.&lt;/li>
&lt;li>TSLint is being sunset in favor of ESLint. I expect a similar consolidation for loaders—Babel’s plugin ecosystem plus TS transpilation makes it attractive.&lt;/li>
&lt;/ul>
&lt;p>So I migrated.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-02-153934.jpeg"
alt="Lab notebook"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>package.json&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/core&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.9.6&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/plugin-proposal-class-properties&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.8.3&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/plugin-proposal-object-rest-spread&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.9.6&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/plugin-syntax-dynamic-import&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.8.3&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/preset-env&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.9.6&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/preset-react&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.9.4&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/preset-typescript&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.9.0&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@babel/runtime&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^7.9.6&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;babel-loader&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;^8.1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>babel.config.js&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">api&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">api&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cache&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">presets&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;@babel/preset-env&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">targets&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">chrome&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;58&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ie&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;11&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;@babel/preset-react&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;@babel/preset-typescript&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;@babel/plugin-syntax-dynamic-import&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;@babel/proposal-class-properties&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;@babel/proposal-object-rest-spread&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>webpack.common.js&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;babel-loader&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After updating configs, rebuilding and running worked exactly as before.&lt;/p>
&lt;h3 id="build-speed">
&lt;a class="heading-anchor-link" href="#build-speed">Build Speed&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-speed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Benchmarks: &lt;a href="https://www.reddit.com/r/typescript/comments/bmz5m7/webpack_speedtests_with_tsloader_babel_7/" target="_blank" rel="noopener">https://www.reddit.com/r/typescript/comments/bmz5m7/webpack_speedtests_with_tsloader_babel_7/&lt;/a>&lt;/p>
&lt;p>Babel’s architecture suggests better performance, and in practice I saw improvements.&lt;/p>
&lt;h2 id="faq">
&lt;a class="heading-anchor-link" href="#faq">FAQ&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="faq"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="tsc-can-transpile-alreadywhy-use-babel">
&lt;a class="heading-anchor-link" href="#tsc-can-transpile-alreadywhy-use-babel">“TSC can transpile already—why use Babel?”&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tsc-can-transpile-alreadywhy-use-babel"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>tsc&lt;/code> does type checking as well as transpilation. &lt;code>@babel/plugin-transform-typescript&lt;/code> focuses on syntax only; type checking still happens via &lt;code>tsc --noEmit&lt;/code> or ESLint.&lt;/li>
&lt;li>Babel provides granular syntax transforms tailored to target browsers.&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://2ality.com/2019/10/babel-loader-typescript.html" target="_blank" rel="noopener">Compiling TypeScript via webpack and babel-loader&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/TypeStrong/ts-loader" target="_blank" rel="noopener">ts-loader&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/babel/babel-loader" target="_blank" rel="noopener">babel-loader&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.reddit.com/r/typescript/comments/bmz5m7/webpack_speedtests_with_tsloader_babel_7/" target="_blank" rel="noopener">Benchmark: ts-loader vs babel 7&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://devblogs.microsoft.com/typescript/typescript-and-babel-7/" target="_blank" rel="noopener">TypeScript and Babel 7&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://segmentfault.com/a/1190000021188054" target="_blank" rel="noopener">@babel/preset-env vs @babel/plugin-transform-runtime&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Improving Redux Saga Error Readability</title><link>https://en.1991421.cn/2020/05/01/redux-saga/</link><pubDate>Fri, 01 May 2020 16:14:12 +0800</pubDate><guid>https://en.1991421.cn/2020/05/01/redux-saga/</guid><description>&lt;blockquote>
&lt;p>When Redux-Saga encounters errors during execution, they are actually very hard to understand. The official documentation mentions this, and the solution is to introduce babel-plugin-redux-saga to improve readability.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-01-172231.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Let&amp;rsquo;s get started&lt;/p>
&lt;h2 id="plugin-installation">
&lt;a class="heading-anchor-link" href="#plugin-installation">Plugin Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugin-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ yarn add babel-plugin-redux-saga -D
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Although the version number is already 1.1.2 &lt;code>complaining about the official's poor semver management&lt;/code>, the official says this is still beta, so it&amp;rsquo;s not robust and has risks&lt;/li>
&lt;li>But since this only serves development debugging, I personally think the risk is not high&lt;/li>
&lt;/ul>
&lt;h2 id="webpack-configuration">
&lt;a class="heading-anchor-link" href="#webpack-configuration">Webpack Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;babel-loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;babel-plugin-redux-saga&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="note-1">
&lt;a class="heading-anchor-link" href="#note-1">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>babel-plugin-redux-saga must be introduced as a babel-loader plugin. If the project uses ts-loader, it won&amp;rsquo;t work. The solution is either to switch ts-loader to babel-loader, or directly add babel-loader.&lt;/p>
&lt;h2 id="effect">
&lt;a class="heading-anchor-link" href="#effect">Effect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="effect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-01-161716.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Notice that the file names and line numbers of effects will be printed out&lt;/p>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://redux-saga.js.org/docs/Troubleshooting.html" target="_blank" rel="noopener">https://redux-saga.js.org/docs/Troubleshooting.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Implementing a Loading Middleware with Redux-Saga</title><link>https://en.1991421.cn/2020/04/15/redux-sagaloading/</link><pubDate>Wed, 15 Apr 2020 23:34:00 +0800</pubDate><guid>https://en.1991421.cn/2020/04/15/redux-sagaloading/</guid><description>&lt;blockquote>
&lt;p>Some flows in our project take time. For a single API call, interceptors can toggle a loading spinner. But when an effect chains multiple requests and additional logic, API-level loading flickers on and off. We need loading control at the saga level.&lt;/p>
&lt;/blockquote>
&lt;h2 id="goal">
&lt;a class="heading-anchor-link" href="#goal">Goal&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="goal"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When an action handled by a saga fires, show a loading mask. When the effect finishes, hide it.&lt;/p>
&lt;h2 id="implementation">
&lt;a class="heading-anchor-link" href="#implementation">Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">put&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;redux-saga/effects&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@redux-saga/is&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">LOADING_BLACKLIST&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">UserActionTypes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">GET_USERS&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">isForkEffect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">is&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">eff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">type&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;FORK&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">loadingWrapper() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadingStatusAction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadingStatusAction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">LOADING_BLACKLIST&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">type&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// @ts-ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadingWrapper&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">effectMiddleware&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">isForkEffect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">eff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">safe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">eff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">eff&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">sagaMiddleware&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createSagaMiddleware&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">effectMiddlewares&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">effectMiddleware&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onError&lt;/span>: &lt;span class="kt">sagaEffectUnhandled&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;code>loading&lt;/code> is a higher-order saga. It wraps the original saga function with “loading on/off” dispatches.&lt;/li>
&lt;li>Since saga functions are generators, the wrapper continues to return a generator.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Without middleware you’d repeat spinner toggling everywhere, violating DRY. This approach keeps the codebase clean.&lt;/p></description></item><item><title>Beginning CSS Preprocessors: Key Takeaways</title><link>https://en.1991421.cn/2020/04/14/beginning-css-preprocessors/</link><pubDate>Tue, 14 Apr 2020 12:06:36 +0800</pubDate><guid>https://en.1991421.cn/2020/04/14/beginning-css-preprocessors/</guid><description>&lt;blockquote>
&lt;p>I recently quickly read a book called &amp;ldquo;Beginning CSS Preprocessors.&amp;rdquo; The book&amp;rsquo;s explanations of Less, Sass, and Compass usage are slightly outdated, but overall, I still gained several insights and ideas from reading it. I&amp;rsquo;ll mark them down here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-04-14-174815.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="the-mission-and-destiny-of-preprocessors">
&lt;a class="heading-anchor-link" href="#the-mission-and-destiny-of-preprocessors">The Mission and Destiny of Preprocessors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-mission-and-destiny-of-preprocessors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>CSS doesn&amp;rsquo;t follow the DRY principle, so in practice, our written styles contain a lot of repetition. The mission of preprocessors is to improve maintainability, flexibility, and be as DRY as possible.&lt;/li>
&lt;li>Preprocessors merely transform one form of style writing into the target CSS format, which means that preprocessors don&amp;rsquo;t add new functionality to styles; they only improve the development experience. We often use TypeScript instead of JavaScript for development now, but it still compiles to JavaScript in the end. From this perspective, the two are similar.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-04-14-183611.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="should-we-use-preprocessors">
&lt;a class="heading-anchor-link" href="#should-we-use-preprocessors">Should We Use Preprocessors?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="should-we-use-preprocessors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If a website is overly simple, whether to use preprocessors or not doesn&amp;rsquo;t matter much. But as site styles become more complex, preprocessors are indeed worth using for maintainability. With variables, nesting, functions, and mixins, we can significantly reduce the amount of CSS code.&lt;/p>
&lt;h2 id="similarities-and-differences-between-less-and-sass">
&lt;a class="heading-anchor-link" href="#similarities-and-differences-between-less-and-sass">Similarities and Differences Between Less and Sass&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="similarities-and-differences-between-less-and-sass"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Less was released after Sass. To lower the learning curve, Less is compatible with CSS syntax.&lt;/li>
&lt;li>Less&amp;rsquo;s bracket structure influenced Sass, which later absorbed some of Less&amp;rsquo;s advantages and released SCSS. So technologies influence each other.&lt;/li>
&lt;li>Sass supports logical conditionals, while Less doesn&amp;rsquo;t. From this perspective, Sass is more powerful. But as mentioned above, being powerful only improves the development experience; Less can achieve the same output styles.&lt;/li>
&lt;/ol>
&lt;h2 id="compass-is-no-longer-maintained">
&lt;a class="heading-anchor-link" href="#compass-is-no-longer-maintained">Compass Is No Longer Maintained&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compass-is-no-longer-maintained"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Compass has not been maintained since August 2016, but strictly speaking, it&amp;rsquo;s not dead; it just exists in another form, even if just as an idea.&lt;/p>
&lt;p>To digress a bit, the MacBook series has now been discontinued by Apple, but the unibody design and USB-C ports of the MacBook have been applied and upgraded in later MBPs. So, technologies are all the same in this regard.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-04-14-221308.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="mac-comes-with-a-ruby-environment">
&lt;a class="heading-anchor-link" href="#mac-comes-with-a-ruby-environment">Mac Comes with a Ruby Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mac-comes-with-a-ruby-environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Ruby is a programming language&lt;/li>
&lt;li>Mac systems come with Ruby pre-installed. For example, my current system is macOS 10.15.4, and the Ruby version is &lt;code>3.0.3&lt;/code>. Additionally, Mac also comes with a Python environment. Why does Mac preinstall Ruby and Python? I think it&amp;rsquo;s because some built-in applications &lt;code>perhaps Xcode&lt;/code> actually use them.&lt;/li>
&lt;li>Gem is Ruby&amp;rsquo;s package manager, providing a standard format for distributing Ruby programs and libraries, as well as a tool for managing package installation.&lt;/li>
&lt;/ol>
&lt;h2 id="css-sprites">
&lt;a class="heading-anchor-link" href="#css-sprites">CSS Sprites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="css-sprites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The purpose of CSS sprites is to reduce network requests, lower webpage request overhead, and display target images through positioning.&lt;/li>
&lt;li>Currently, when we use sprites, we can actually achieve this directly through webpack plugins.&lt;/li>
&lt;/ol>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>These are the insights I gained after reading this book.&lt;/li>
&lt;li>I&amp;rsquo;ve been too busy lately and haven&amp;rsquo;t read enough. I have to say, reading is indeed a good way to systematically supplement knowledge and broaden horizons.&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://css-tricks.com/css-sprites/" target="_blank" rel="noopener">CSS Sprites: What They Are, Why They&amp;rsquo;re Cool, and How To Use Them&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.ruanyifeng.com/blog/2012/11/compass.html" target="_blank" rel="noopener">Compass Usage Guide&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Case Styles in Frontend Programming</title><link>https://en.1991421.cn/2020/04/11/frontend-casing-style/</link><pubDate>Sat, 11 Apr 2020 23:01:29 +0800</pubDate><guid>https://en.1991421.cn/2020/04/11/frontend-casing-style/</guid><description>&lt;blockquote>
&lt;p>Style often doesn&amp;rsquo;t have absolute right or wrong, but in a project or a team, having too many diverse styles is definitely wrong. For individuals, in actual programming, you will gradually develop your own style. Here&amp;rsquo;s a summary of what I know to throw some light on this topic.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-04-11-230805.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="common-case-styles">
&lt;a class="heading-anchor-link" href="#common-case-styles">Common case styles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-case-styles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>PascalCase (Upper Camel Case) &lt;code>UserType&lt;/code>&lt;/li>
&lt;li>camelCase (Lower Camel Case) &lt;code>userType&lt;/code>&lt;/li>
&lt;li>kebab-case (Hyphen-Separated Case) &lt;code>user-type&lt;/code>&lt;/li>
&lt;li>snake_case (Lower Snake Case) &lt;code>user_type&lt;/code>&lt;/li>
&lt;li>SNAKE_CASE (Upper Snake Case) &lt;code>USER_TYPE&lt;/code>&lt;/li>
&lt;li>dot.annotation (Dot Notation) &lt;code>user.type&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="practice">
&lt;a class="heading-anchor-link" href="#practice">Practice&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practice"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="component-names">
&lt;a class="heading-anchor-link" href="#component-names">Component names&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-names"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>kebab case&lt;/code> (also known as hyphen-separated case)&lt;/p>
&lt;h3 id="tsjs-constants">
&lt;a class="heading-anchor-link" href="#tsjs-constants">TS/JS constants&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tsjs-constants"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>upper snake case&lt;/code>&lt;/p>
&lt;h3 id="classes-and-interfaces">
&lt;a class="heading-anchor-link" href="#classes-and-interfaces">Classes and interfaces&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="classes-and-interfaces"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>PascalCase&lt;/code>&lt;/p>
&lt;h3 id="css-class-names">
&lt;a class="heading-anchor-link" href="#css-class-names">CSS class names&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="css-class-names"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>kebab case&lt;/code>&lt;/p>
&lt;h3 id="static-asset-files">
&lt;a class="heading-anchor-link" href="#static-asset-files">Static asset files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="static-asset-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>lower snake case&lt;/code>, such as images, Excel download templates, etc.&lt;/p>
&lt;h3 id="jsts-filenames">
&lt;a class="heading-anchor-link" href="#jsts-filenames">JS/TS filenames&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jsts-filenames"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>kebab case&lt;/code> (also known as hyphen-separated case)&lt;/p>
&lt;h3 id="i18n-keys">
&lt;a class="heading-anchor-link" href="#i18n-keys">i18n keys&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="i18n-keys"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>dot annotation&lt;/code>&lt;/p>
&lt;h3 id="urls">
&lt;a class="heading-anchor-link" href="#urls">URLs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="urls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>kebab case&lt;/code>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Regarding practice, I personally think:&lt;/p>
&lt;ol>
&lt;li>Learn industry-standard specifications and respect the experience contributed by predecessors through trials and tribulations. For example, why URLs don&amp;rsquo;t use underscores, why constants use upper snake case, etc. These are all details with reasons.&lt;/li>
&lt;li>Some issues that seem irrelevant but constantly reveal their importance in actual development, such as naming. After standardizing and getting used to it, you will benefit continuously.&lt;/li>
&lt;li>Some things and rules don&amp;rsquo;t have absolute right or wrong. Appropriately develop a style that suits yourself.&lt;/li>
&lt;/ol>
&lt;p>Let&amp;rsquo;s encourage each other.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841" target="_blank" rel="noopener">Case Styles: Camel, Pascal, Snake, and Kebab Case&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/49263762/is-there-a-name-for-dot-separated-case" target="_blank" rel="noopener">Is there a name for dot-separated case?&lt;/a>ty&lt;/li>
&lt;/ul></description></item><item><title>[Translation] Event Handler Naming in React</title><link>https://en.1991421.cn/2020/04/11/react/</link><pubDate>Sat, 11 Apr 2020 14:50:00 +0800</pubDate><guid>https://en.1991421.cn/2020/04/11/react/</guid><description>&lt;blockquote>
&lt;p>Naming is always challenging. We need meaning, abstraction, and brevity. I read a great article on event handler naming conventions and found it aligned with my own practice, but explained it better. I translated it here as a review and to help other developers.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-04-11-184938.png"
alt="Event Handler Naming in React - Fig 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Even though this is a translation, I still recommend reading the original:&lt;/p>
&lt;p>&lt;a href="https://jaketrent.com/post/naming-event-handlers-react/" target="_blank" rel="noopener">EVENT HANDLER NAMING IN REACT&lt;/a>&lt;/p>
&lt;h2 id="props">
&lt;a class="heading-anchor-link" href="#props">Props&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="props"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When defining prop names, I usually prefix with &lt;code>on&lt;/code>, like &lt;code>onClick&lt;/code>. This matches the JS event model and keeps event handlers consistent.&lt;/p>
&lt;h2 id="function-naming">
&lt;a class="heading-anchor-link" href="#function-naming">Function naming&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="function-naming"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For function names, I follow the same pattern but use &lt;code>handle**&lt;/code> instead of &lt;code>on**&lt;/code>, e.g., &lt;code>handleClick&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">MyComponent&lt;/span> &lt;span class="na">onClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleClick&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>on&lt;/code> indicates the event binding, and &lt;code>handle&lt;/code> indicates the handler.&lt;/p>
&lt;p>I also keep the same verb like Click, so we can tell it&amp;rsquo;s a click event. Some people name it handleDismiss for an alert close, but I prefer matching the action. This mapping is clear. Semantic naming aligns with the JS event model.&lt;/p>
&lt;h2 id="more-complex-naming">
&lt;a class="heading-anchor-link" href="#more-complex-naming">More complex naming&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="more-complex-naming"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It probably won&amp;rsquo;t get more complex than &lt;a href="https://zh.wikipedia.org/wiki/%E8%A5%BF%E7%8F%AD%E7%89%99%E8%AF%AD%E5%A7%93%E5%90%8D" target="_blank" rel="noopener">this&lt;/a>, but imagine more events and more logic.&lt;/p>
&lt;p>For example with alerts and forms:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onAlertClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleAlertClick&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onFormSubmit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleFormSubmit&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I prefer noun first, then verb. It also groups nicely when sorted.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onAlertClick&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleAlertClick&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onAlertHover&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">handleAlertHover&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="component-splitting">
&lt;a class="heading-anchor-link" href="#component-splitting">Component splitting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-splitting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>How to split components is a big topic and an art. From a naming perspective, if a module has many handlers, it lacks abstraction. We should split it for better encapsulation. Example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onRegistrationSubmit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onLoginSubmit&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After splitting, naming is simpler:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">onSubmit in registration-form.js
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">onSubmit in login-form.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="built-in-event-names">
&lt;a class="heading-anchor-link" href="#built-in-event-names">Built-in event names&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="built-in-event-names"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Some handlers are built-in, like onClick and onSubmit, which fire on button or form.&lt;/p>
&lt;p>When passing these names into custom components, be careful. You might not want the component to be clickable.&lt;/p>
&lt;p>Why? Because we might spread props.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">MyComponent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="na">...props&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>&lt;span class="nx">Stuff&lt;/span> &lt;span class="nx">that&lt;/span> &lt;span class="nx">might&lt;/span> &lt;span class="nx">really&lt;/span> &lt;span class="nx">need&lt;/span> &lt;span class="nx">the&lt;/span> &lt;span class="nx">onClick&lt;/span>&lt;span class="p">...&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If I pass an onClick prop to a component for internal use, but also spread all props, I must be careful with naming. I don&amp;rsquo;t want the div to be clickable.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>I agree with a point from Refactoring: variable naming needs thought and can evolve during refactoring. It is not random.&lt;/li>
&lt;li>The author&amp;rsquo;s naming ideas are excellent. After splitting components, the context allows simpler names, e.g., onRegistrationSubmit to onSubmit. Similarly, when iterating a country array, I don&amp;rsquo;t like naming the item variable &lt;code>country&lt;/code> because context already provides that. Naming it &lt;code>item&lt;/code> is fine and aids reuse. If the array changes to users, you don&amp;rsquo;t need to rename the variable.&lt;/li>
&lt;li>Be flexible. Code is like speech: don&amp;rsquo;t say redundant things.&lt;/li>
&lt;li>This article gave me a systematic naming approach for frontend event handlers. Still, apply flexibly.&lt;/li>
&lt;/ul></description></item><item><title>window.open Basics</title><link>https://en.1991421.cn/2020/04/02/window-open/</link><pubDate>Thu, 02 Apr 2020 09:27:34 +0800</pubDate><guid>https://en.1991421.cn/2020/04/02/window-open/</guid><description>&lt;blockquote>
&lt;p>&lt;code>window.open&lt;/code> loads a new resource. We typically open a web URL. During code review I saw this:&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;../../static/template.xlsx&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Project structure:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│── home
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   └── banner
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   └── index.tsx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── static
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── template.xlsx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is problematic. After build, &lt;code>static&lt;/code> is top-level. The correct path should be &lt;code>static/template.xlsx&lt;/code>.&lt;/p>
&lt;p>But the key point: I thought this wouldn’t work locally since it’s not a proper web path but a project path. Yet it did work—revealing a gap in my understanding. Notes below.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>URL&lt;/p>
&lt;blockquote>
&lt;p>A Uniform Resource Locator (URL) is a reference to a web resource that specifies its location and a retrieval mechanism. A URL is a specific type of URI. URLs most commonly reference web pages (http), but also ftp, mailto, JDBC, etc.&lt;/p>
&lt;/blockquote>
&lt;p>A URL can address static or dynamic resources.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>URLs can be relative or absolute&lt;/p>
&lt;p>You can use &lt;code>..&lt;/code> and the browser resolves relative paths to absolute ones.&lt;/p>
&lt;p>These two URLs are equivalent, note the &lt;code>../&lt;/code> in the second:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>&lt;a href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/soutu/img/camera_new_x2_fb6c085.png" target="_blank" rel="noopener">https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/soutu/img/camera_new_x2_fb6c085.png&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/../cache/static/protocol/https/soutu/img/camera_new_x2_fb6c085.png" target="_blank" rel="noopener">https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/../cache/static/protocol/https/soutu/img/camera_new_x2_fb6c085.png&lt;/a>&lt;/p>
&lt;pre>&lt;code>```
&lt;/code>&lt;/pre>
&lt;ol start="3">
&lt;li>
&lt;p>Why both dev and build paths “work” in development&lt;/p>
&lt;p>3.1 The local Webpack dev server exposes the project as a web service without restrictions, so &lt;code>localhost:3000/**&lt;/code> is routable. Production hosting often adds security restrictions.&lt;/p>
&lt;p>3.2 In dev, TypeScript files exist as separate files. Webpack can resolve &lt;code>../../static/template.xlsx&lt;/code>, so it “works”.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>In dev, &lt;code>static/template.xlsx&lt;/code> “works” even though it’s not the real dev file path because of &lt;code>CopyWebpackPlugin&lt;/code>:&lt;/p>
&lt;pre>&lt;code>```javascript
new CopyWebpackPlugin([
...
{ from: './src/main/webapp/static/', to: 'static' },
...
])
```
&lt;/code>&lt;/pre>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The original path was wrong, but with loaders/plugins auto-handling assets, it may still work—similar to CSS-in-JS pipelines.&lt;/li>
&lt;li>Use small details like this to reinforce understanding.&lt;/li>
&lt;/ol></description></item><item><title>JS Precision Math: Choosing big.js vs mathjs</title><link>https://en.1991421.cn/2020/04/01/js-big-js-mathjs/</link><pubDate>Wed, 01 Apr 2020 23:38:11 +0800</pubDate><guid>https://en.1991421.cn/2020/04/01/js-big-js-mathjs/</guid><description>&lt;blockquote>
&lt;p>Recently I needed precision calculations in a project. Native number decimals lose precision (0.1 + 0.2 is not 0.3). So I needed a library. I considered two options and chose &lt;code>mathjs&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/Ywkg9Jy.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="bigjs">
&lt;a class="heading-anchor-link" href="#bigjs">big.js&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bigjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>A small, fast JavaScript library for arbitrary-precision decimal arithmetic.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>Size: 5.9 KB minified, 2.7 KB gzipped&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/Q8XQdw8.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The size above is from CDN usage.&lt;/p>
&lt;h3 id="pros">
&lt;a class="heading-anchor-link" href="#pros">Pros&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pros"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Small size&lt;/li>
&lt;/ol>
&lt;p>&lt;a href="https://github.com/MikeMcl/big.js" target="_blank" rel="noopener">See here&lt;/a>&lt;/p>
&lt;h2 id="mathjs">
&lt;a class="heading-anchor-link" href="#mathjs">mathjs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mathjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types like numbers, big numbers, complex numbers, fractions, units, and matrices. Powerful and easy to use.&lt;/p>
&lt;/blockquote>
&lt;p>Size: Unpacked Size 10.2 MB&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/rlltCY0.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="pros-1">
&lt;a class="heading-anchor-link" href="#pros-1">Pros&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pros-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Expression parsing&lt;/li>
&lt;/ol>
&lt;h3 id="compatibility">
&lt;a class="heading-anchor-link" href="#compatibility">Compatibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="compatibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Math.js works on any ES5 compatible JavaScript engine: node.js, Chrome, Firefox, Safari, Edge, and IE11.&lt;/p>
&lt;p>&lt;a href="https://github.com/josdejong/mathjs" target="_blank" rel="noopener">See here&lt;/a>&lt;/p>
&lt;h3 id="size-optimization">
&lt;a class="heading-anchor-link" href="#size-optimization">Size optimization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="size-optimization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>If you only need numbers, you can import a specific build such as &lt;code>mathjs/main/esm/number&lt;/code> instead of full &lt;code>mathjs&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="my-view">
&lt;a class="heading-anchor-link" href="#my-view">My view&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="my-view"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the calculation is very simple (A * B), big.js is enough. But for complex calculations, big.js hurts readability. mathjs is more readable.&lt;/p>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Here is a formula. With mathjs you can write it like this:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/zJWBP2y.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>But with big.js, it becomes much less readable. Size can be mitigated by CDN and caching, so I chose mathjs.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/U9pQ2EC.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Both work. Use what fits.&lt;/p></description></item><item><title>Eliminating Dead Code in TS Projects</title><link>https://en.1991421.cn/2020/03/29/tsdead-code/</link><pubDate>Sun, 29 Mar 2020 22:52:46 +0800</pubDate><guid>https://en.1991421.cn/2020/03/29/tsdead-code/</guid><description>&lt;blockquote>
&lt;p>Real-world projects inevitably accumulate dead code. It bloats the bundle a bit, but the bigger pain is chasing down why a change “doesn’t work” only to realize the code never runs. My rule of thumb: delete it all. If someone says, “What if we need it someday?” my answer is, “What day exactly? We have Git history for that—don’t hide behind laziness or lack of professionalism. Delete it.”&lt;/p>
&lt;/blockquote>
&lt;p>Teams have different experience levels and perspectives. Instead of repeatedly cleaning up after others, enforce rules—and maybe share an article like this one to back up the rationale.&lt;/p>
&lt;p>Alright, let’s dive in.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/rWPKXiP.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="built-in-tsconfig-options">
&lt;a class="heading-anchor-link" href="#built-in-tsconfig-options">Built-in TSConfig options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="built-in-tsconfig-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The TypeScript compiler ships with two flags that help cut down dead code.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;compilerOptions&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;noUnusedLocals&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;noUnusedParameters&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="nounusedlocals">
&lt;a class="heading-anchor-link" href="#nounusedlocals">noUnusedLocals&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nounusedlocals"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Unused local variables trigger an error, as shown below:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/IuDMVrf.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="nounusedparameters">
&lt;a class="heading-anchor-link" href="#nounusedparameters">noUnusedParameters&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nounusedparameters"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Unused function parameters trigger an error as well:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/Z6GHChP.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="how-to-avoid-the-errors">How to avoid the errors&lt;/h4>&lt;p>Sometimes you genuinely need a placeholder parameter, such as in callbacks where the index of each argument matters. In that case, prefix the unused parameter with an underscore. The TypeScript compiler ignores those, and naming them with numbers helps clarify their position.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/q8gMMhU.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="ts-rules">
&lt;a class="heading-anchor-link" href="#ts-rules">TS rules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ts-rules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="tslint-no-commented-code-rule">
&lt;a class="heading-anchor-link" href="#tslint-no-commented-code-rule">tslint-no-commented-code-rule&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tslint-no-commented-code-rule"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Some folks comment out code with promises to clean it later—rarely happens. This rule flags commented code longer than two lines.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/QpzqwUi.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="configuration">Configuration&lt;/h4>&lt;ol>
&lt;li>
&lt;p>yarn add tslint-no-commented-code-rule -D&lt;/p>
&lt;/li>
&lt;li>
&lt;p>tslint.json&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;no-commented-code&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;minLineCount&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ignoredCommentRegex&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^(\\w+$|TODO|FIXME)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="no-unused-variable">
&lt;a class="heading-anchor-link" href="#no-unused-variable">&lt;del>no-unused-variable&lt;/del>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="no-unused-variable"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This rule is no longer recommended.&lt;/p>
&lt;h2 id="ts-unused-exports">
&lt;a class="heading-anchor-link" href="#ts-unused-exports">ts-unused-exports&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ts-unused-exports"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the compiler settings and lint rules above, a lot of cruft disappears. One gap remains: unused exports (functions, classes, constants).&lt;/p>
&lt;p>Lint rules work on a single file at a time, so they can’t detect unused exports. You’d want support at the TypeScript compiler level, but it’s not there yet. See the discussions:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/microsoft/TypeScript/issues/13408" target="_blank" rel="noopener">https://github.com/microsoft/TypeScript/issues/13408&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/microsoft/TypeScript/issues/29293" target="_blank" rel="noopener">https://github.com/microsoft/TypeScript/issues/29293&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>I considered building something until I found an existing tool: &lt;a href="https://github.com/pzavolinsky/ts-unused-exports" target="_blank" rel="noopener">ts-unused-exports&lt;/a>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/tars59f.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="configuration-1">
&lt;a class="heading-anchor-link" href="#configuration-1">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>yarn add -D ts-unused-exports&lt;/li>
&lt;li>ts-unused-exports ./tsconfig.json &amp;ndash;showLineNumber &amp;ndash;ignoreTestFiles&lt;/li>
&lt;/ol>
&lt;p>Wire it into your hooks—&lt;code>pre-commit&lt;/code>, for example—to keep dead code from ever landing.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>These settings keep most redundancy out of the codebase. Rules help, but leveling up the team matters even more.&lt;/li>
&lt;li>Clean code is an ongoing pursuit—this is just one small step. Keep spotting and fixing issues.&lt;/li>
&lt;/ol></description></item><item><title>A Guide to HAR Files: Recording and Analyzing Web Requests</title><link>https://en.1991421.cn/2020/03/28/chrome-har/</link><pubDate>Sat, 28 Mar 2020 17:44:43 +0800</pubDate><guid>https://en.1991421.cn/2020/03/28/chrome-har/</guid><description>&lt;blockquote>
&lt;p>For ordinary users, Chrome is just a web browser, but it can be considered a productivity tool for developers.&lt;/p>
&lt;p>Recently, I couldn&amp;rsquo;t reproduce a bug. The only clue was an issue at the data level when the user requested data on the page. To verify this, I needed to understand the user&amp;rsquo;s situation. So, I asked the business contact to access the system using Chrome, download the HAR file, and send it to me. Once I received the file, I quickly identified and resolved the issue.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>That&amp;rsquo;s right, the key point is HAR. Let&amp;rsquo;s briefly introduce it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="har">
&lt;a class="heading-anchor-link" href="#har">HAR&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="har"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The full name is &lt;code>HTTP Archive format&lt;/code>, a file format that records network request information.&lt;/p>
&lt;h2 id="har-upload-and-download-operations">
&lt;a class="heading-anchor-link" href="#har-upload-and-download-operations">HAR Upload and Download Operations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="har-upload-and-download-operations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="download">
&lt;a class="heading-anchor-link" href="#download">Download&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>While accessing the web, open the console tools, ensure that Chrome is making web requests, and click the download button to save it locally.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-22-135811.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="upload">
&lt;a class="heading-anchor-link" href="#upload">Upload&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upload"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Still in the console, click the upload button and select the HAR file.&lt;/p>
&lt;h3 id="things-to-know">
&lt;a class="heading-anchor-link" href="#things-to-know">Things to Know&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="things-to-know"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>The downloaded HAR includes all types of network requests, whether XHR, JS, or CSS.&lt;/li>
&lt;li>HAR only records logs, so when uploading, it only restores them and does not make the requests again.&lt;/li>
&lt;li>The scope of HAR recorded is limited to what is displayed in the console; if the console is cleared, the downloaded file will also be empty.&lt;/li>
&lt;li>HAR can be uploaded on any page.&lt;/li>
&lt;/ol>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This is just a brief introduction, but I want to emphasize that using Chrome effectively can be very effective.&lt;/p>
&lt;h2 id="docs">
&lt;a class="heading-anchor-link" href="#docs">Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/HAR_%28file_format%29" target="_blank" rel="noopener">HAR (file format)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>React Project Framework Upgrade</title><link>https://en.1991421.cn/2020/03/23/react/</link><pubDate>Mon, 23 Mar 2020 23:23:19 +0800</pubDate><guid>https://en.1991421.cn/2020/03/23/react/</guid><description>&lt;blockquote>
&lt;p>Open source is free, but upgrades require caution. They have risks, but if you understand what each upgrade includes (fix/feat/breaking change), risks can be reduced. Long term, tech must keep up with the times, especially during product iterations.&lt;/p>
&lt;p>The web project I&amp;rsquo;m working on entered a new iteration, so I upgraded the framework over the weekend.&lt;/p>
&lt;/blockquote>
&lt;h2 id="motivation">
&lt;a class="heading-anchor-link" href="#motivation">Motivation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="motivation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Upgrading isn&amp;rsquo;t just about changing version numbers. It&amp;rsquo;s about the changes behind them: features, performance, patterns.&lt;/p>
&lt;p>Here are areas I care about most.&lt;/p>
&lt;h2 id="key-areas-of-concern">
&lt;a class="heading-anchor-link" href="#key-areas-of-concern">Key areas of concern&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-areas-of-concern"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="typescript">
&lt;a class="heading-anchor-link" href="#typescript">TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Optional chaining&lt;/li>
&lt;/ol>
&lt;p>We used to write:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sayHello&lt;/span>&lt;span class="o">&amp;amp;&amp;amp;&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sayHello&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we can write:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">user&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sayHello&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Nullish coalescing&lt;/li>
&lt;/ol>
&lt;p>We used to write:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">pete&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">username&lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="nx">alan&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">username&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we can write:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">pete&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">username&lt;/span>&lt;span class="o">??&lt;/span>&lt;span class="nx">alan&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">username&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Isn&amp;rsquo;t this the same? No. If the value is 0, &lt;code>||&lt;/code> treats it as false, while &lt;code>??&lt;/code> only treats null/undefined as empty. So they differ.&lt;/p>
&lt;h3 id="react">
&lt;a class="heading-anchor-link" href="#react">React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Scheduler&lt;/li>
&lt;li>lazy / Suspense&lt;/li>
&lt;li>Hooks&lt;/li>
&lt;/ol>
&lt;h3 id="other-benefits">
&lt;a class="heading-anchor-link" href="#other-benefits">Other benefits&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-benefits"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Newer versions have better docs and bug tracking.&lt;/li>
&lt;li>Newer antd versions bring features.&lt;/li>
&lt;li>react-redux v7 claims significant performance improvements.&lt;/li>
&lt;/ol>
&lt;h2 id="issues-encountered">
&lt;a class="heading-anchor-link" href="#issues-encountered">Issues encountered&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="issues-encountered"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Upgrading is never just version changes - there are pitfalls. Here are a few.&lt;/p>
&lt;/blockquote>
&lt;h3 id="optional-chaining-caused-lint-errors">
&lt;a class="heading-anchor-link" href="#optional-chaining-caused-lint-errors">Optional chaining caused lint errors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="optional-chaining-caused-lint-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>New TypeScript syntax caused lint failures, so lint tooling must be upgraded.&lt;/p>
&lt;h3 id="prettier-new-default-changes">
&lt;a class="heading-anchor-link" href="#prettier-new-default-changes">Prettier new default changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prettier-new-default-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Note:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">trailingComma: none
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="react-dom-and-react-version-alignment">
&lt;a class="heading-anchor-link" href="#react-dom-and-react-version-alignment">react-dom and react version alignment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-dom-and-react-version-alignment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When upgrading these two, ensure versions match or errors occur.&lt;/p>
&lt;p>There were also minor issues, like type errors after upgrading Antd, which were fixed case by case.&lt;/p>
&lt;h2 id="packages-upgraded">
&lt;a class="heading-anchor-link" href="#packages-upgraded">Packages upgraded&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packages-upgraded"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>TypeScript &lt;code>~3.3.1 =&amp;gt; ~3.8.3&lt;/code>&lt;/li>
&lt;li>react &lt;code>16.4.2=&amp;gt; 16.8.3&lt;/code>&lt;/li>
&lt;li>react-dom &lt;code>16.4.2 =&amp;gt; 16.8.3&lt;/code>&lt;/li>
&lt;li>react-redux &lt;code>6.0.1 =&amp;gt; 7.2.0&lt;/code>&lt;/li>
&lt;li>antd &lt;code>3.20.0&lt;/code>=&amp;gt;&lt;code>3.26.13&lt;/code>&lt;/li>
&lt;li>prettier &lt;code>1.14.3&lt;/code>=&amp;gt;&lt;code>^2.0.5&lt;/code>&lt;/li>
&lt;li>tslint &lt;code>5.18.0&lt;/code>=&amp;gt;&lt;code>^6.1.1&lt;/code>&lt;/li>
&lt;li>tslint-react &lt;code>4.0.0&lt;/code>=&amp;gt;&lt;code>^5.0.0&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After upgrading, basic tests passed - perfect.&lt;/p>
&lt;p>Upgrades are always about finding pitfalls, stepping into them, and then fixing them. But the benefits are clear:&lt;/p>
&lt;ol>
&lt;li>More future possibilities because the tech is updated&lt;/li>
&lt;li>Smooth upgrades mean deeper understanding of the stack&lt;/li>
&lt;/ol>
&lt;p>So, just do it.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/formatjs/react-intl/blob/master/docs/Upgrade-Guide.md" target="_blank" rel="noopener">https://github.com/formatjs/react-intl/blob/master/docs/Upgrade-Guide.md&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/formatjs/react-intl/blob/master/docs/Components.md" target="_blank" rel="noopener">https://github.com/formatjs/react-intl/blob/master/docs/Components.md&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/facebook/react/blob/master/CHANGELOG.md" target="_blank" rel="noopener">https://github.com/facebook/react/blob/master/CHANGELOG.md&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.typescriptlang.org/tsconfig" target="_blank" rel="noopener">https://www.typescriptlang.org/tsconfig&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>integrity in yarn.lock</title><link>https://en.1991421.cn/2020/03/19/yarn-lockintegrity/</link><pubDate>Thu, 19 Mar 2020 23:00:57 +0800</pubDate><guid>https://en.1991421.cn/2020/03/19/yarn-lockintegrity/</guid><description>&lt;blockquote>
&lt;p>When committing code you may notice diffs in yarn.lock adding or removing &lt;code>integrity&lt;/code> fields. Here’s a quick rundown of what it is.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/ZuxPmBc.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/H7gXr8t.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="purpose">
&lt;a class="heading-anchor-link" href="#purpose">Purpose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="purpose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Ensures resource integrity (package version and content). After Yarn downloads a resource, it compares the computed integrity to the one in the file; if they don’t match, installation fails.&lt;/p>
&lt;h2 id="how-is-integrity-computed">
&lt;a class="heading-anchor-link" href="#how-is-integrity-computed">How is integrity computed?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-is-integrity-computed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Typically: hash(bytes) → base64‑encode.&lt;/p>
&lt;h2 id="why-isnt-it-on-every-package">
&lt;a class="heading-anchor-link" href="#why-isnt-it-on-every-package">Why isn’t it on every package?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-isnt-it-on-every-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In theory, it should be present for every package.&lt;/p>
&lt;h3 id="v194">
&lt;a class="heading-anchor-link" href="#v194">v1.9.4&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="v194"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Reproduced at the time: delete lockfile, run &lt;code>yarn install&lt;/code>, some packages still lacked &lt;code>integrity&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/m8wleBK.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="v1224">
&lt;a class="heading-anchor-link" href="#v1224">v1.22.4&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="v1224"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After upgrading, retrying showed every package had &lt;code>integrity&lt;/code>.
&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/X65ldCD.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>A version bug — upgrading resolves it.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/53540429/what-is-the-integrity-property-inside-yarn-lock-file" target="_blank" rel="noopener">What is the integrity property inside yarn.lock file?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/Security/%E5%AD%90%E8%B5%84%E6%BA%90%E5%AE%8C%E6%95%B4%E6%80%A7" target="_blank" rel="noopener">Subresource Integrity&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://w3c.github.io/webappsec-subresource-integrity/#resource-integrity" target="_blank" rel="noopener">Resource Integrity&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Introducing CheckStyle to Improve Java Code Quality</title><link>https://en.1991421.cn/2020/03/15/checkstylejava/</link><pubDate>Sun, 15 Mar 2020 20:33:57 +0800</pubDate><guid>https://en.1991421.cn/2020/03/15/checkstylejava/</guid><description>&lt;blockquote>
&lt;p>Recently participating in some backend development, I felt the backend code was somewhat messy, with issues like unused imports, unused variables, unreasonable access modifiers, etc. I&amp;rsquo;ve complained about these issues multiple times before, but always procrastinated on setting standards and promoting standardization. So, taking advantage of the weekend, I made up my mind to tackle this.&lt;/p>
&lt;p>Unlike Java, the frontend JS/TS part is effectively controlled at the style level due to Husky+Tslint control. Can&amp;rsquo;t the backend do the same? It definitely can.&lt;/p>
&lt;/blockquote>
&lt;h2 id="checkstyle">
&lt;a class="heading-anchor-link" href="#checkstyle">CheckStyle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="checkstyle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>What to use for control? Regarding technology selection, it seems without question - I previously reviewed ThoughtWorks&amp;rsquo; 2019 technology stack statistical report, and for code style control, most chose CheckStyle.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/mTleaK6.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>OK, following the crowd.&lt;/p>
&lt;h3 id="usage-methods">
&lt;a class="heading-anchor-link" href="#usage-methods">Usage Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Regarding how to use it, there are several ways:&lt;/p>
&lt;ol>
&lt;li>IDEA plugin&lt;/li>
&lt;li>Maven plugin&lt;/li>
&lt;li>Custom scripts&lt;/li>
&lt;/ol>
&lt;h3 id="choosing-scripts">
&lt;a class="heading-anchor-link" href="#choosing-scripts">Choosing Scripts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="choosing-scripts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After consideration, I decided to implement with scripts, for the following reasons:&lt;/p>
&lt;ol>
&lt;li>The IDEA plugin&amp;rsquo;s current file scanning scope is all files, and doesn&amp;rsquo;t support scanning only changed files or files in stage. &lt;code>The project is open source, so interested parties can submit PRs to support this&lt;/code>&lt;/li>
&lt;li>Maven plugin can run during the build packaging phase or testing phase, but cannot detect during pre-commit&lt;/li>
&lt;li>Scripts can be written as pre-commit hooks. But a new developer needs to manually execute commands to place the detection hook under .git/hooks.&lt;/li>
&lt;/ol>
&lt;p>With the approach determined, let&amp;rsquo;s get started.&lt;/p>
&lt;h2 id="specific-configuration">
&lt;a class="heading-anchor-link" href="#specific-configuration">Specific Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="specific-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The required configuration files are as follows:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/ykQ6mrs.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>init.sh is for development initialization deployment, the content is just a copy command. &lt;code>This doesn't support Windows default shell, but git bash works&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>#!/bin/bash -e
cp pre-commit ../../.git/hooks/
chmod +x ../../.git/hooks/pre-commit&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. pre-commit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> The main detection relies on this. For the specific code, [click here](https://gist.github.com/alanhe421/baa359d064b17988bb8cd89a0bbe4c2e). __Note, the file has no extension. The name is a Git hook and cannot be changed.__
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. checkstyle.jar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This is the checkstyle program package
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. google_checks.xml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Custom code style configuration. To modify or add rules, edit this directly.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">## Application
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">### Mac || Linux Users
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">In IDE, select init.sh, right-click and execute
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">### Windows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Manually copy `pre-commit` to `../../.git/hooks/`
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">## Implementation Effect
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">When committing, if there are errors, the commit fails and will prompt specific error files and line/column numbers.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">![](https://i.imgur.com/3JOKl7Q.png)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Just that refreshing.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">## Frontend Hook Tool Husky
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">I&amp;#39;ve always had a question: how does husky integrate with hooks?
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">To detect during Git commits, you must deal with hooks. The above operation ultimately puts the checkstyle command we want to execute into the pre-commit hook. So frontend Husky must work the same way, right?
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">![](https://i.imgur.com/WnFB45q.png)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">As shown above, when we install the husky package, it actually creates a bunch of Git hooks, and the content is all about executing script commands in husky. You can understand that the script command parameters are the configuration interface husky provides us. Thinking this way, it&amp;#39;s actually consistent with our approach above.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Our executing `init.sh` is equivalent to `npm i husky`
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">## Is Forcibly Controlling Code Style Necessary?
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">As above, we happily controlled code style. Of course, because it depends on git hooks, if someone just turns off hooks when committing, there&amp;#39;s really no way, I&amp;#39;ve encountered such teammates before.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Admittedly, we can also add detection during build packaging. So after illegal commits that bypass hooks, we can still make these code builds fail and turn red.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">But the problem is, these people don&amp;#39;t realize the value of unified code style. I think we can only use this image to inspire these people.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">![](https://i.imgur.com/5VEPZ8j.jpg)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">## Final Thoughts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">After the above configuration, I finally solved a technology stack issue I&amp;#39;d been dragging on, and gained these two insights:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. Java code style assurance now has an initial solution, and of course can be further optimized, such as whether some errors can be automatically fixed?
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. Gained more understanding of Git&amp;#39;s hook mechanism, and also understood how husky is implemented.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">## Reference Links
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1. [Introduction to CheckStyle](https://www.baeldung.com/checkstyle-java)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. [Using Checkstyle to Check Code Style](https://juejin.im/post/5bcb56286fb9a05d0e2e9bbc)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. [Automatic checkstyle check before git commit](https://jimolonely.github.io/2018/09/28/java/007-checkstyle-idea-git-pre-commit/)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. [Saving Java Code Style OCD](http://insights.thoughtworkers.org/save-java-code-style-obsessive-compulsive-disorder/)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Deep Cloning in Redux State</title><link>https://en.1991421.cn/2020/03/07/redux-statedeep-clone/</link><pubDate>Sat, 07 Mar 2020 00:02:56 +0800</pubDate><guid>https://en.1991421.cn/2020/03/07/redux-statedeep-clone/</guid><description>&lt;blockquote>
&lt;p>A recent frontend performance issue surfaced: interactions became sluggish and eventually blew up memory. The culprit was, unsurprisingly, us—and the weapon of choice was &lt;code>cloneDeep&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/Tuu8qLW.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Consider this reducer:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">updateDetailReducer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span>: &lt;span class="kt">IDetailState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">detail&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cloneDeep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">detail&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Does it work? Functionally, yes—the state updates as expected.&lt;/p>
&lt;p>But it’s terrible for performance because of the deep clone. Every property inside &lt;code>detail&lt;/code> becomes a brand-new object. If dozens of connected components read &lt;code>detail&lt;/code> or its children, the page crawls. The more interactions, the worse it gets. Remember: when component props change, React re-renders. Most of those props didn’t change, yet deep cloning forces every related component to re-render, wasting cycles and killing performance.&lt;/p>
&lt;h2 id="shouldcomponentupdate">
&lt;a class="heading-anchor-link" href="#shouldcomponentupdate">&lt;code>shouldComponentUpdate&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="shouldcomponentupdate"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Deep cloning triggers a storm of re-renders because React’s &lt;code>shouldComponentUpdate&lt;/code> performs a shallow comparison (basic types by value, objects by reference). Fresh references everywhere mean React thinks everything changed.&lt;/p>
&lt;h2 id="lodash-clonedeep">
&lt;a class="heading-anchor-link" href="#lodash-clonedeep">Lodash &lt;code>cloneDeep&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lodash-clonedeep"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since we used Lodash’s &lt;code>cloneDeep&lt;/code>, let’s contrast it with &lt;code>clone&lt;/code> and the native &lt;code>JSON.parse(JSON.stringify())&lt;/code>. I’ve hit errors deep cloning with &lt;code>JSON.parse(JSON.stringify())&lt;/code> that &lt;code>cloneDeep&lt;/code> handled, so they definitely behave differently.&lt;/p>
&lt;h3 id="clonedeep">
&lt;a class="heading-anchor-link" href="#clonedeep">&lt;code>cloneDeep&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="clonedeep"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Iterates recursively:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;xxx&amp;#39;&lt;/span>&lt;span class="p">}};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cloneDeep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="clone">
&lt;a class="heading-anchor-link" href="#clone">&lt;code>clone&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="clone"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Non-iterative copy:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;xxx&amp;#39;&lt;/span>&lt;span class="p">}};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">clone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// false
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// true
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="jsonparsejsonstringify">
&lt;a class="heading-anchor-link" href="#jsonparsejsonstringify">&lt;code>JSON.parse(JSON.stringify())&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jsonparsejsonstringify"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Similar to &lt;code>cloneDeep&lt;/code>, but only works with numbers, strings, and plain objects without functions or Symbols.&lt;/p>
&lt;p>So for pure deep clone behavior, &lt;code>cloneDeep&lt;/code> is more complete and safer.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;xxx&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="nx">formatter&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;@&amp;#39;&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">formatter&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// function
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">formatter&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// undefined
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Fixing the bug was quick, but the lesson sticks.&lt;/p>
&lt;ol>
&lt;li>Redux uses a single state tree. Each reducer updates part of that tree, but that doesn’t mean you should clone the entire branch.&lt;/li>
&lt;li>Deep clones aren’t forbidden in reducers—but they’re rarely necessary. The higher up you clone, the more components you invalidate. Use them sparingly.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://medium.com/@timoshenko.evgeny/deep-cloning-of-redux-state-dc9ac8ff618c" target="_blank" rel="noopener">Deep-cloning of Redux state&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.samanthaming.com/tidbits/70-3-ways-to-clone-objects/" target="_blank" rel="noopener">3 Ways to Clone Objects in JavaScript&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/using_should_component_update.html" target="_blank" rel="noopener">Using shouldComponentUpdate()&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>JavaScript method vs function vs member</title><link>https://en.1991421.cn/2020/03/01/javascript-method-vs-function-vs-member/</link><pubDate>Sun, 01 Mar 2020 17:01:49 +0800</pubDate><guid>https://en.1991421.cn/2020/03/01/javascript-method-vs-function-vs-member/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;ve always been unclear about these three concepts, so I decided to organize my understanding.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/IOvJYBA.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="method">
&lt;a class="heading-anchor-link" href="#method">Method&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="method"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>A method is a function which is a property of an object. There are two kind of methods: Instance Methods which are built-in tasks performed by an object instance, or Static Methods which are tasks that are called directly on an object constructor.&lt;/p>
&lt;p>A method is a function that is a property of an object. There are two types of methods: Instance Methods are built-in tasks performed by an object instance, and Static Methods are tasks called directly on an object constructor.&lt;/p>
&lt;/blockquote>
&lt;p>From MDN&lt;/p>
&lt;h2 id="function">
&lt;a class="heading-anchor-link" href="#function">Function&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="function"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Generally speaking, a function is a &amp;ldquo;subprogram&amp;rdquo; that can be called by code external (or internal in the case of recursion) to the function. Like the program itself, a function is composed of a sequence of statements called the function body. Values can be passed to a function, and the function will return a value.&lt;/p>
&lt;p>In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. What distinguishes them from other objects is that functions can be called. In brief, they are Function objects.&lt;/p>
&lt;/blockquote>
&lt;p>From MDN&lt;/p>
&lt;h2 id="member">
&lt;a class="heading-anchor-link" href="#member">Member&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="member"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The concept of a member is reflected in an interface or abstract class. A member can be a method or a variable value.&lt;/p>
&lt;p>Personal understanding&lt;/p>
&lt;h2 id="difference-between-function-method-and-constructor-calls">
&lt;a class="heading-anchor-link" href="#difference-between-function-method-and-constructor-calls">Difference between Function, Method and Constructor calls&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="difference-between-function-method-and-constructor-calls"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In the book &lt;code>Effective JavaScript&lt;/code>, there&amp;rsquo;s a section that states:&lt;/p>
&lt;blockquote>
&lt;p>If you&amp;rsquo;re familiar with object-oriented programming, you&amp;rsquo;re likely accustomed to thinking of functions, methods, and class constructors as three separate things. In JavaScript, these are just three different usage patterns of one single construct: functions.&lt;/p>
&lt;/blockquote>
&lt;p>This means that &lt;code>functions, methods, and class constructors are just three different usage patterns of a single construct: functions&lt;/code>.&lt;/p>
&lt;h3 id="important-notes">
&lt;a class="heading-anchor-link" href="#important-notes">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Method calls use the object that contains the method property as the call receiver.&lt;/li>
&lt;li>Function calls use the global object (or undefined in strict mode) as their receiver. Function calls are rarely used to call methods.&lt;/li>
&lt;li>Constructors need to be called with the new operator and produce a new object as their receiver.&lt;/li>
&lt;/ol>
&lt;h3 id="nonmethod">
&lt;a class="heading-anchor-link" href="#nonmethod">nonmethod&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nonmethod"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Some methods are not called on a specific object - these are called nonmethods, as shown below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">hello&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;hello, &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">username&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">hello&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// &amp;#34;hello, undefined&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="tslint---prefer-function-over-method">
&lt;a class="heading-anchor-link" href="#tslint---prefer-function-over-method">tslint - prefer-function-over-method&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tslint---prefer-function-over-method"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In tslint rules, there&amp;rsquo;s a rule that warns when methods don&amp;rsquo;t use &lt;code>this&lt;/code>, suggesting to change them to functions.&lt;/p>
&lt;p>Why? There&amp;rsquo;s a great explanation on StackOverflow.&lt;/p>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// TypeScript
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">class&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fn1() {&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fn2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// JavaScript
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">A() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">A&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">A&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}());&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If we inherit from this class and want to override the fn2 function, what do we do?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">B&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">private&lt;/span> &lt;span class="nx">oldFn2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fn2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn2&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now let&amp;rsquo;s change fn2 to a method and see how to override it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fn1() {&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fn2() {&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">B&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">A&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fn2() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fn2&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Methods and functions are different after compilation.&lt;/li>
&lt;li>From a performance perspective, &lt;code>functions are oriented towards the class object rather than each instance of the class, making them more memory-friendly.&lt;/code>&lt;/li>
&lt;li>The rule&amp;rsquo;s meaning is that if a method in a class instance doesn&amp;rsquo;t use &lt;code>this&lt;/code>, it means every instance is the same, which is wasteful in terms of overhead, so it&amp;rsquo;s recommended to switch to functions.&lt;/li>
&lt;/ol>
&lt;h2 id="implement-members-vs-override-methods">
&lt;a class="heading-anchor-link" href="#implement-members-vs-override-methods">Implement Members vs Override methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implement-members-vs-override-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>IDEA or WebStorm&amp;rsquo;s code generation features can improve efficiency. In TypeScript classes, when invoking the generation shortcut, you&amp;rsquo;ll notice two options: &lt;code>Implement Members&lt;/code> and &lt;code>Override methods&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/GjO75od.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>So what&amp;rsquo;s the difference between these two?&lt;/p>
&lt;ol>
&lt;li>
&lt;p>As mentioned in the member section, members are more reflected in abstract classes and interfaces. When we write implementations, they become useful. For example, when we declare an interface class with properties A and B, and instantiate an interface variable.
&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/89FT1SJ.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>When writing component classes, such as wanting to override hooks, we need to choose override methods because React.Component is a class, not an interface, and it has complete implementations.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>At this point, my understanding seems to have been further enhanced.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Glossary/Method" target="_blank" rel="noopener">Method - MDN&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.jetbrains.com/help/rider/Code_Generation__Implementing_Overriding_Methods.html" target="_blank" rel="noopener">Implementing/Overriding Members&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/39048796/function-declarations-or-expressions-for-class-methods" target="_blank" rel="noopener">Function declarations or expressions for class methods?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://learning.oreilly.com/library/view/effective-javascript-68/9780132902281/ch03.html" target="_blank" rel="noopener">effective javascript&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Git Team Development Process Specification - MR Conflict Resolution</title><link>https://en.1991421.cn/2020/02/27/git-2/</link><pubDate>Thu, 27 Feb 2020 19:06:54 +0800</pubDate><guid>https://en.1991421.cn/2020/02/27/git-2/</guid><description>&lt;blockquote>
&lt;p>Recently, our frontend team encountered multiple code loss issues during code merging. Based on this phenomenon, we conducted analysis and finally established a conflict resolution process.&lt;/p>
&lt;/blockquote>
&lt;h2 id="conflict-resolution-process">
&lt;a class="heading-anchor-link" href="#conflict-resolution-process">Conflict Resolution Process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conflict-resolution-process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/Wso19as.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Example&lt;/strong>&lt;/p>
&lt;p>A conflict occurs between feat/xxx and sprint.&lt;/p>
&lt;ol>
&lt;li>Create a new conflict resolution branch called &lt;code>resolve-conflict&lt;/code> - the name indicates it&amp;rsquo;s for handling conflicts, making it easier to trace later. Point the &lt;code>feat/xxx&lt;/code> MR to &lt;code>resolve-conflict&lt;/code>&lt;/li>
&lt;li>All relevant parties for the conflicting code work together to resolve the conflict&lt;/li>
&lt;li>Merge the &lt;code>resolve-conflict&lt;/code> branch into &lt;code>sprint&lt;/code>, then delete &lt;code>resolve-conflict&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="principles">
&lt;a class="heading-anchor-link" href="#principles">Principles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="principles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>MRs that aren&amp;rsquo;t very urgent should be handled collectively. When conflicts occur, all relevant parties should review and handle them together to avoid code loss.&lt;/p>
&lt;h2 id="why-this-approach">
&lt;a class="heading-anchor-link" href="#why-this-approach">Why This Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-this-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Handling conflicts locally: For protected branches, we don&amp;rsquo;t want to allow direct pushes. Creating a new branch can bypass this issue&lt;/li>
&lt;li>If conflicts are resolved directly in sprint, there&amp;rsquo;s a risk of encountering new node conflicts during submission, requiring additional conflict resolution. This makes the entire git history difficult to understand and not conducive to later problem tracing&lt;/li>
&lt;/ol></description></item><item><title>Effective JavaScript Reading Notes</title><link>https://en.1991421.cn/2020/02/24/effective-javascript/</link><pubDate>Mon, 24 Feb 2020 08:39:37 +0800</pubDate><guid>https://en.1991421.cn/2020/02/24/effective-javascript/</guid><description>&lt;blockquote>
&lt;p>I finally finished reading this book intermittently&lt;/p>
&lt;p>This book explains JavaScript key concepts in a recipe-like format. I learned a lot from it, so I&amp;rsquo;m making notes here&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/BwrqWcB.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="versions">
&lt;a class="heading-anchor-link" href="#versions">Versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>ES is the standard, JS is the implementation. Since JS in web pages executes on the client side, different clients (browsers) have different support for JS, HTML, and CSS. This is why we have compatibility issues - the same JS code may produce different results in different browsers.&lt;/li>
&lt;li>With TypeScript and Babel available now, we can actually write newer ES versions like ES6, 7, 8 in development, then use TSC and Babel to compile to target JS versions, while adding polyfills for unsupported syntax.&lt;/li>
&lt;/ol>
&lt;h2 id="numeric-computation">
&lt;a class="heading-anchor-link" href="#numeric-computation">Numeric Computation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="numeric-computation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Unlike other languages such as Java and Python, JavaScript has only one numeric type for all numbers including integers and decimals — &lt;code>Number&lt;/code>. It follows the IEEE 754 standard, using 64-bit fixed length (8 bytes) representation, which is the standard double-precision floating-point number (compared to float 32-bit single precision)&lt;/li>
&lt;li>Theoretically, it&amp;rsquo;s impossible to guarantee precision when storing infinite decimals in finite space. So precision issues are inevitable, like with our 1/3.&lt;/li>
&lt;li>Due to limited space, there&amp;rsquo;s also the &lt;code>big number crisis&lt;/code>. Since 64 bits are finite, the maximum value is fixed at &lt;code>9007199254740991&lt;/code>. What happens when we exceed it? The crisis is inevitable, but fortunately there&amp;rsquo;s a new built-in type BigInt&lt;/li>
&lt;li>BigInt serves exactly what its name implies =&amp;gt; for larger integer values, though it&amp;rsquo;s still 8 bytes&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/tuALQHR.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-10-150502.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note: This issue doesn&amp;rsquo;t only occur in JavaScript. Almost all programming languages use IEEE-754 floating-point representation, and any programming language using binary floating-point numbers will have this problem. But languages like Java have BigDecimal class for direct use, so the problem is less prominent.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-10-154451.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="floating-point-precision-issues">
&lt;a class="heading-anchor-link" href="#floating-point-precision-issues">Floating-Point Precision Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="floating-point-precision-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use existing libraries, recommend big.js &amp;amp; mathjs. The principle is to convert floating-point numbers to strings and simulate actual arithmetic processes&lt;/p>
&lt;h2 id="prefer-primitive-types-over-wrapper-objects">
&lt;a class="heading-anchor-link" href="#prefer-primitive-types-over-wrapper-objects">Prefer Primitive Types Over Wrapper Objects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prefer-primitive-types-over-wrapper-objects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>For example, when declaring strings, we can use &lt;code>new String('hello')&lt;/code>, &lt;code>String('hello')&lt;/code>, &lt;code>'hello'&lt;/code>, but note that new String is actually a String object, not a primitive data type.&lt;/li>
&lt;li>For primitive types, generally don&amp;rsquo;t use new object wrappers for declaration.&lt;/li>
&lt;/ul>
&lt;h2 id="limitations-of-semicolon-insertion">
&lt;a class="heading-anchor-link" href="#limitations-of-semicolon-insertion">Limitations of Semicolon Insertion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="limitations-of-semicolon-insertion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In the following code, sample1 runs OK, but sample2 doesn&amp;rsquo;t return the expected result. Why? Because of automatic semicolon insertion (ASI), which takes effect during JS syntax parsing&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;ESLint&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">website&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;eslint.org&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">website&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">function sayName(){
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">return
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> name: &amp;#34;ESLint&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">};
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">console.log(sayName()); // undefined
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>ASI is a fallback (second choice). When encountering line terminators, the compiler always first tries to parse the symbol stream separated by line terminators as a single statement (with a few exceptions: return, throw, break, continue, yield, ++, &amp;ndash;). Only when it doesn&amp;rsquo;t conform to correct syntax does it fall back to enabling the ASI mechanism, treating the line terminator-separated symbol stream as two statements (commonly called &amp;ldquo;inserting semicolons&amp;rdquo;).&lt;/li>
&lt;li>The aforementioned exceptions don&amp;rsquo;t allow line terminators to exist. If there&amp;rsquo;s a line terminator in a statement, the parser will prioritize considering the line terminator as the end of the statement, which is called restricted production in the ECMAScript standard.&lt;/li>
&lt;/ul>
&lt;h2 id="variable-scope">
&lt;a class="heading-anchor-link" href="#variable-scope">Variable Scope&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="variable-scope"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h2 id="higher-order-functions">
&lt;a class="heading-anchor-link" href="#higher-order-functions">Higher-Order Functions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="higher-order-functions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The characteristic of higher-order functions is taking functions as parameters or returning functions as results. Methods like Array.map and sort all accept a function. Higher-order functions are a way to abstract code and improve reusability. So, worth practicing.&lt;/p>
&lt;h3 id="arrays-and-dictionaries">
&lt;a class="heading-anchor-link" href="#arrays-and-dictionaries">Arrays and Dictionaries&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="arrays-and-dictionaries"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Compared to dictionaries, arrays have order, while dictionaries don&amp;rsquo;t. When using for&amp;hellip;in on dictionary data, the order might not be what we expect, as shown in the example below. This is because during traversal, keys are parseFloat&amp;rsquo;d, values are obtained and then sorted.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">userMap&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hello&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;alan&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;1&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;helen&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">key&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nx">userMap&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// hello
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="notes-1">
&lt;a class="heading-anchor-link" href="#notes-1">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Object keys are always strings, even when traversing an array&lt;/li>
&lt;/ul>
&lt;h2 id="undefined-values">
&lt;a class="heading-anchor-link" href="#undefined-values">undefined Values&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="undefined-values"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Like Java, C and other languages that represent &amp;ldquo;null&amp;rdquo; values, JavaScript is special in having both null and undefined.&lt;/p>
&lt;h3 id="similarities">
&lt;a class="heading-anchor-link" href="#similarities">Similarities&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="similarities"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Equality testing between null and undefined results in true, &lt;code>console.log(null==undefined); \\ true&lt;/code>, undefined value is derived from null value.&lt;/p>
&lt;h2 id="concurrency">
&lt;a class="heading-anchor-link" href="#concurrency">Concurrency&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concurrency"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>JavaScript implements single-threaded execution to avoid complexity&lt;/li>
&lt;li>Browsers are multi-process, that&amp;rsquo;s why Chrome consumes so many resources&lt;/li>
&lt;li>Whether it&amp;rsquo;s multithreading, multiprocessing, or coroutines, they&amp;rsquo;re all for better utilization of computer resources&lt;/li>
&lt;/ul>
&lt;h3 id="processes">
&lt;a class="heading-anchor-link" href="#processes">Processes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="processes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>A process is an execution instance of an application. Each process consists of private virtual address space, code, data, and other system resources. Processes can request, create, and use system resources (such as independent memory areas) during execution, and these resources are destroyed when the process terminates. Threads are independent execution units within a process. Different threads can share process resources, so in multithreaded situations, special attention must be paid to access control of critical resources.&lt;/p>
&lt;/blockquote>
&lt;p>As shown below in system process management, we can see Chrome has more than one process.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-05-10-224423.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="jss-single-threaded-nature-doesnt-contradict-promiseall-parallel-loading">
&lt;a class="heading-anchor-link" href="#jss-single-threaded-nature-doesnt-contradict-promiseall-parallel-loading">JS&amp;rsquo;s Single-Threaded Nature Doesn&amp;rsquo;t Contradict Promise.all Parallel Loading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jss-single-threaded-nature-doesnt-contradict-promiseall-parallel-loading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Promise is a pattern, a way to organize asynchronous code. It solves callback hell but is still asynchronous.&lt;/li>
&lt;li>Promise.all executes a bunch of asynchronous operations, then retrieves them all at once. The N asynchronous operations started by Promise.all don&amp;rsquo;t affect each other&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>One mark of an excellent developer is that they can always explain why something is designed or done a certain way, while non-excellent developers often can&amp;rsquo;t articulate the reasoning.&lt;/li>
&lt;li>After finishing this book, I feel JavaScript isn&amp;rsquo;t that difficult, or rather, high-level languages aren&amp;rsquo;t that difficult. Understanding their design purposes and patterns should make them much easier to work with.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.im/post/59e85eebf265da430d571f89" target="_blank" rel="noopener">Understanding the JavaScript execution mechanism&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E6%B5%AE%E7%82%B9%E6%95%B0" target="_blank" rel="noopener">Floating-point number&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/camsong/blog/issues/9" target="_blank" rel="noopener">JavaScript floating-point pitfalls and solutions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5c493ebb6fb9a049c0436077" target="_blank" rel="noopener">Notes on JS calculation errors&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pelli.ren/gitbooks/my_js_part/js/effective.javascript.html" target="_blank" rel="noopener">Reading: 68 Effective Ways to Write High-Quality JavaScript&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.edureka.co/blog/type-casting-in-java/" target="_blank" rel="noopener">What is Typecasting in Java and how does it work?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.runoob.com/w3cnote/js-precision-problem-and-solution.html" target="_blank" rel="noopener">JavaScript precision issues and solutions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.ctolib.com/docs/sfile/effective-javascript/index.html" target="_blank" rel="noopener">https://www.ctolib.com/docs/sfile/effective-javascript/index.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Important Details to Note When Using Antd</title><link>https://en.1991421.cn/2020/02/20/antd/</link><pubDate>Thu, 20 Feb 2020 22:49:56 +0800</pubDate><guid>https://en.1991421.cn/2020/02/20/antd/</guid><description>&lt;h2 id="table-rowkey">
&lt;a class="heading-anchor-link" href="#table-rowkey">Table-rowKey&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="table-rowkey"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>index.js:1 Warning: [antd: Table] Each record in dataSource of table should have a unique &lt;code>key&lt;/code> prop, or set &lt;code>rowKey&lt;/code> of Table to an unique primary key, see &lt;a href="https://u.ant.design/table-row-key" target="_blank" rel="noopener">https://u.ant.design/table-row-key&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>When using the Table component, we often see this warning in the browser console, which occurs because the row key is missing.&lt;/p>
&lt;p>If rowKey is not specified, it &lt;code>defaults to rowIndex&lt;/code>. So if the records truly don&amp;rsquo;t have any unique fields, you can leave it unspecified.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getRecordKey&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span>: &lt;span class="kt">T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">rowKey&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">recordKey&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">rowKey&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;function&amp;#39;&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">rowKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="kt">any&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="nx">rowKey&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">warning&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">recordKey&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Table&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Each record in dataSource of table should have a unique `key` prop, &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;or set `rowKey` of Table to an unique primary key, &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;see https://u.ant.design/table-row-key&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">recordKey&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">index&lt;/span> : &lt;span class="kt">recordKey&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that if you specified a rowKey but still get a warning about missing keys, it&amp;rsquo;s likely because the key doesn&amp;rsquo;t exist.&lt;/p>
&lt;h2 id="key-in-table-column">
&lt;a class="heading-anchor-link" href="#key-in-table-column">key in Table-column&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-in-table-column"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Unique key of this column, you can ignore this prop if you&amp;rsquo;ve set a unique dataIndex&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>A missing key won't trigger warning errors.&lt;/code>&lt;/p>
&lt;p>If you&amp;rsquo;ve configured dataIndex, you can omit the key, because when the key is undefined, it will use the &lt;code>column index&lt;/code> instead.&lt;/p>
&lt;p>Here&amp;rsquo;s part of the antd source code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">columns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">columns&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">column&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">newColumn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">column&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">newColumn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getColumnKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newColumn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">newColumn&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getColumnKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">column&lt;/span>: &lt;span class="kt">ColumnProps&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span> &lt;span class="nx">index?&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">column&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">key&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">column&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dataIndex&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="three-parameters-in-table-column-render">
&lt;a class="heading-anchor-link" href="#three-parameters-in-table-column-render">Three parameters in Table-column render&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="three-parameters-in-table-column-render"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official Antd documentation describes render like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">text&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We&amp;rsquo;re accustomed to implementing it with these three parameters, but is this always necessary? Not really.&lt;/p>
&lt;p>For example, if a column doesn&amp;rsquo;t have dataIndex configured, in the actual render, we can do:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isHidden&lt;/span>&lt;span class="o">?&lt;/span> &lt;span class="nx">record.a&lt;/span> : &lt;span class="kt">record.b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Antd UI components are all encapsulated RC component libraries, mostly just modifying the UI. The column rendering logic is primarily in the rc-table source code.&lt;/p>
&lt;p>In the source code, when getting the first parameter value for the column cell render function, it will first try to use dataIndex. If dataIndex doesn&amp;rsquo;t exist and is not a number basic type, it will return the current row record.&lt;/p>
&lt;p>&lt;code>The conclusion is that when dataIndex doesn't exist and is not a number type, when the cell render actually executes, the first parameter value will be the record. But index is always the third parameter.&lt;/code>&lt;/p>
&lt;p>src/Cell/index.tsx&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">getPathValue&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">object&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">React.ReactNode&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">RecordType&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dataIndex&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>src/utils/valueUtil.tsx&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">getPathValue&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ValueType&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">ObjectType&lt;/span> &lt;span class="na">extends&lt;/span> &lt;span class="na">object&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">record&lt;/span>: &lt;span class="kt">ObjectType&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>: &lt;span class="kt">DataIndex&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">ValueType&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Skip if path is empty
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">path&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">path&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s1">&amp;#39;number&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="kt">unknown&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">ValueType&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">pathList&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">toArray&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">current&lt;/span>: &lt;span class="kt">ValueType&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">ObjectType&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="nx">pathList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">current&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">prop&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">pathList&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">current&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">current&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">prop&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">current&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">ValueType&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="uniqueness-of-value-in-treeselect-treenode">
&lt;a class="heading-anchor-link" href="#uniqueness-of-value-in-treeselect-treenode">Uniqueness of value in TreeSelect-TreeNode&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="uniqueness-of-value-in-treeselect-treenode"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When constructing TreeNode data, note that &lt;code>value must be unique&lt;/code>. The direct problem with duplicate values is display errors, because different nodeTitles corresponding to the same value will cause the component to display the latter one.&lt;/p>
&lt;p>If your actual data indeed has the same values, analyze the differences - for example, add fixed prefixes or suffixes to create distinctions between them.&lt;/p>
&lt;p>When duplicate values occur, you&amp;rsquo;ll get a warning, so if you encounter this situation, you definitely need to address it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/4VczqoW.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="validator-in-form">
&lt;a class="heading-anchor-link" href="#validator-in-form">validator in Form&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="validator-in-form"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In a validator, returning true or false indicates whether the validation passes, and error messages depend on callback(new Error()). However, note that &lt;code>if the validator execution throws an error, the exception information won't be caught by the program. What appears to happen is that form.validate blocks indefinitely, and the program doesn't continue. The console also won't show any error messages.&lt;/code>&lt;/p>
&lt;h2 id="setfieldsvalue-in-form">
&lt;a class="heading-anchor-link" href="#setfieldsvalue-in-form">setFieldsValue in Form&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setfieldsvalue-in-form"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="setfieldsvalue-during-componentwillreceiveprops">
&lt;a class="heading-anchor-link" href="#setfieldsvalue-during-componentwillreceiveprops">setFieldsValue during componentWillReceiveProps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setfieldsvalue-during-componentwillreceiveprops"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When actually controlling form values, setFieldsValue is frequently used. However, note that if the component itself is wrapped with Form.create, don&amp;rsquo;t use the setFieldsValue method in the componentWillReceiveProps lifecycle, otherwise you may enter an infinite loop and cause memory leaks.&lt;/p>
&lt;p>The reason can be understood this way: Form&amp;rsquo;s higher-order function wraps our component and indirectly calls componentWillReceiveProps, so setFieldsValue &amp;lt;=&amp;gt; componentWillReceiveProps would create an infinite loop.&lt;/p>
&lt;p>Why do I say &amp;ldquo;may&amp;rdquo;? If you strengthen the conditional checks, it might not necessarily create an infinite loop. For example:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/TF1Hji7.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>For details, &lt;a href="https://github.com/ant-design/ant-design/issues/2985" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;p>Of course, if the form component and the operation executing setFieldsValue are not in the same component, there&amp;rsquo;s generally no problem.&lt;/p></description></item><item><title>Thoughts on Code Coverage Scope in Code Review</title><link>https://en.1991421.cn/2020/02/19/codereview/</link><pubDate>Wed, 19 Feb 2020 22:54:51 +0800</pubDate><guid>https://en.1991421.cn/2020/02/19/codereview/</guid><description>&lt;blockquote>
&lt;p>Our current code review scope filters commits by branch and by day. Does that create duplicate reviews in practice?&lt;/p>
&lt;/blockquote>
&lt;p>When we target a single branch—say, a sprint branch—and filter for yesterday&amp;rsquo;s commits, the list includes merge requests (MRs). Each MR is effectively a commit that bundles earlier commits. An MR created on the 5th might contain code written on the 4th.&lt;/p>
&lt;h3 id="do-we-keep-seeing-the-same-code">
&lt;a class="heading-anchor-link" href="#do-we-keep-seeing-the-same-code">Do We Keep Seeing the Same Code?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="do-we-keep-seeing-the-same-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>You&amp;rsquo;ll notice commit dates outside the target review date because an MR on the 5th can include diffs from the 4th.&lt;/li>
&lt;li>Reviews focus on diffs. Once earlier code merges into the branch, the next day&amp;rsquo;s diff excludes it. &lt;code>So the same code isn't reviewed twice.&lt;/code>&lt;/li>
&lt;/ol>
&lt;h3 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The current code review scope doesn&amp;rsquo;t create duplicate coverage.&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>An MR appears as a single commit, but the diff folds in N commits from other branches tied to that MR.&lt;/li>
&lt;li>If the MR has conflicts, &lt;code>the merge commit may include diff adjustments that exist only in that commit&lt;/code>.&lt;/li>
&lt;li>The MR&amp;rsquo;s commit date differs from its constituent commits—the history is linearized.&lt;/li>
&lt;li>Git orders entries by push date, but the list shows commit dates.&lt;/li>
&lt;/ol></description></item><item><title>Why I Hate Lodash</title><link>https://en.1991421.cn/2020/02/16/lodash/</link><pubDate>Sun, 16 Feb 2020 14:35:26 +0800</pubDate><guid>https://en.1991421.cn/2020/02/16/lodash/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/zrcrYJD.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>During project development and code reviews, I consistently recommend that teams reduce their dependence on Lodash and use native JavaScript instead. Whenever I see someone using it, I pay attention and repeatedly ask why they need Lodash. Here I&amp;rsquo;ll explain why I dislike it so much.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>Lodash provides many utility functions, both those you know and those you don&amp;rsquo;t. Overuse leads to over-dependence, and you may end up &lt;code>knowing Lodash but not native JS, preventing proper learning of native JavaScript due to heavy Lodash usage&lt;/code>&lt;/li>
&lt;li>Depending on Lodash realistically means &lt;code>increased bundle size&lt;/code>, though with today&amp;rsquo;s high bandwidth and fast internet speeds, this size increase might not be a major concern&lt;/li>
&lt;li>When both native methods and Lodash can accomplish the same functionality, using Lodash may mean &lt;code>worse performance&lt;/code>.&lt;/li>
&lt;li>Lodash methods consider many edge cases comprehensively, so they have &lt;code>stronger error tolerance&lt;/code>, but you should know that not throwing errors is sometimes more dangerous than throwing them. For example, with find and get methods - no error doesn&amp;rsquo;t necessarily mean correctness.&lt;/li>
&lt;li>Some Lodash methods like the get method actually cause &lt;code>type information loss&lt;/code>, and path access parameters lose static analysis capabilities, making things like refactoring harder to detect.&lt;/li>
&lt;li>Some Lodash methods can cause errors if you&amp;rsquo;re not familiar with them. For example, with isEmpty, you should know that &lt;code>_.isEmpty(true)&lt;/code> returns true&lt;/li>
&lt;/ol>
&lt;h2 id="attitude-towards-using-lodash">
&lt;a class="heading-anchor-link" href="#attitude-towards-using-lodash">Attitude Towards Using Lodash&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="attitude-towards-using-lodash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>For specific functionality, prioritize native JavaScript approaches, considering target browser compatibility&lt;/li>
&lt;li>When importing Lodash, use ES module tree-shaking imports to reduce bundle size to some extent&lt;/li>
&lt;li>I recommend this repository - &lt;a href="https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore" target="_blank" rel="noopener">You-Dont-Need-Lodash-Underscore&lt;/a>. Through this repository, you can understand how experts view Lodash and learn how to implement equivalent functionality natively&lt;/li>
&lt;li>I&amp;rsquo;m not saying don&amp;rsquo;t use it, but I resent and reject mindlessly using Lodash to solve problems without thinking, because I believe these people don&amp;rsquo;t consider whether native solutions exist or think about performance - to put it emotionally, they have no sense of guilt.&lt;/li>
&lt;li>For those blind Lodash fans, what will you do someday when there&amp;rsquo;s no Lodash dependency in your development environment?&lt;/li>
&lt;/ol></description></item><item><title>NPM Dependency Model</title><link>https://en.1991421.cn/2020/02/16/npm-dependency-model/</link><pubDate>Sun, 16 Feb 2020 01:28:18 +0800</pubDate><guid>https://en.1991421.cn/2020/02/16/npm-dependency-model/</guid><description>&lt;blockquote>
&lt;p>I&amp;rsquo;ve always stayed at the simple usage level for dependencies, without understanding the dependency model. So I decided to eliminate this blind spot.&lt;/p>
&lt;/blockquote>
&lt;p>Let&amp;rsquo;s understand through the following scenarios.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/M7HuNia.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="direct-and-indirect-dependencies-on-the-same-package">
&lt;a class="heading-anchor-link" href="#direct-and-indirect-dependencies-on-the-same-package">Direct and Indirect Dependencies on the Same Package&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="direct-and-indirect-dependencies-on-the-same-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;em>Note: Versions are different&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">foo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── hello 0.1.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── bar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── hello 0.2.8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── goodbye 3.4.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="physical-disk">
&lt;a class="heading-anchor-link" href="#physical-disk">Physical Disk&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="physical-disk"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">node_modules/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── hello 0.1.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── bar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── node_modules/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── hello 0.2.8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── goodbye 3.4.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this situation, both versions will coexist on the physical disk.&lt;/p>
&lt;h2 id="indirect-dependencies-on-the-same-package">
&lt;a class="heading-anchor-link" href="#indirect-dependencies-on-the-same-package">Indirect Dependencies on the Same Package&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="indirect-dependencies-on-the-same-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;em>Note: Versions are different&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">foo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── hello 0.1.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── world 1.0.7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── hello 0.2.8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── goodbye 3.4.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this situation, both versions will coexist on the physical disk.&lt;/p>
&lt;h3 id="physical-disk-1">
&lt;a class="heading-anchor-link" href="#physical-disk-1">Physical Disk&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="physical-disk-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">node_modules/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── foo/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── node_modules/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── hello/ 0.1.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── world/ 1.0.7
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── bar/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── node_modules/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── hello/ 0.2.8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── goodbye/ 3.4.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dependency-model-during-install-phase">
&lt;a class="heading-anchor-link" href="#dependency-model-during-install-phase">Dependency Model During Install Phase&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dependency-model-during-install-phase"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>During the install phase, npm installs packages based on Semver, which is package name + version number. As long as they&amp;rsquo;re inconsistent, both versions will be installed.&lt;/p>
&lt;h2 id="build-phase">
&lt;a class="heading-anchor-link" href="#build-phase">Build Phase&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-phase"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As shown above, multiple versions of packages exist in node_modules. So the question arises: which version will be in the vendor file after packaging? &lt;code>Both will be there&lt;/code>, because the two instances are indeed different.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Under npm&amp;rsquo;s dependency model, physically, different versions of the same package are allowed to exist. A package&amp;rsquo;s uniqueness is determined by &lt;code>package name + version number&lt;/code>&lt;/li>
&lt;li>Try to ensure consistent dependency package versions as much as possible, as this affects the overall bundled size&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding this point helps us better use and manage dependencies, giving us a clear understanding of the final dependency versions.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://snyk.io/blog/whats-an-npm-dependency/" target="_blank" rel="noopener">The 5 dimensions of an npm dependency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/" target="_blank" rel="noopener">Understanding the npm dependency model&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Solving the Misalignment Between Antd Table Headers and Content Cells</title><link>https://en.1991421.cn/2020/02/05/antd/</link><pubDate>Wed, 05 Feb 2020 18:15:08 +0800</pubDate><guid>https://en.1991421.cn/2020/02/05/antd/</guid><description>&lt;blockquote>
&lt;p>Recently, I encountered this styling issue, as shown in the image.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/rB3RoI1.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The image shows the problem.&lt;/p>
&lt;h2 id="how-to-reproduce">
&lt;a class="heading-anchor-link" href="#how-to-reproduce">How to Reproduce&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-reproduce"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Fixed table header with percentage width columns.&lt;/p>
&lt;h2 id="investigation-steps">
&lt;a class="heading-anchor-link" href="#investigation-steps">Investigation Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="investigation-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Testing showed that on Mac with Chrome &lt;code>Version 79.0.3945.130&lt;/code>, this issue doesn&amp;rsquo;t occur&lt;/li>
&lt;li>On Windows 10 with Chrome &lt;code>Version 79.0.3945.130&lt;/code>, the issue exists&lt;/li>
&lt;li>On Windows 10 with Edge, there&amp;rsquo;s no issue&lt;/li>
&lt;li>Dragging the browser to an extended screen eliminates the issue&lt;/li>
&lt;/ul>
&lt;p>After comparing these scenarios, I found clues related to &lt;code>system, browser, different screens&lt;/code>, and based on this direction of investigation, I finally determined the cause.&lt;/p>
&lt;h2 id="problem-explanation">
&lt;a class="heading-anchor-link" href="#problem-explanation">Problem Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem-explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;code>PC screen display scaling settings&lt;/code> affect page layout&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/46VkpbC.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If you change it to 100%, the page redraws and the problem disappears&lt;/p>
&lt;/li>
&lt;li>
&lt;p>On Windows, Chrome&amp;rsquo;s scroll pane takes up space by default, but on Mac it doesn&amp;rsquo;t. This is related to the operating system, not the browser version. Why doesn&amp;rsquo;t Mac have this issue? For example, in Chrome on Mac, the scroll pane is not calculated into the width, as if it&amp;rsquo;s on a different layer&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/sNtAceB.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The direct cause of misalignment is that &lt;code>on Windows, when the device pixel ratio is not 1, the vertical scroll pane width of the table header is inconsistent with the scroll pane width of the table content, so the container width and table content width are inconsistent&lt;/code>. Since we set the column widths as percentages, this causes the two widths to be different, resulting in this problem. When we manually change the computer&amp;rsquo;s scaling to 100%, the scroll pane widths become consistent, so there&amp;rsquo;s no misalignment issue.
But why, when the device pixel ratio is not 1, are the table header&amp;rsquo;s vertical scroll pane and the table content&amp;rsquo;s scroll pane widths inconsistent? I don&amp;rsquo;t know, maybe both are bugs.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="device-pixel-ratio">
&lt;a class="heading-anchor-link" href="#device-pixel-ratio">Device Pixel Ratio&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="device-pixel-ratio"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Now that we understand the problem, let&amp;rsquo;s explore the underlying cause: &lt;em>device pixel ratio&lt;/em>.&lt;/p>
&lt;p>When we modify the scaling, we&amp;rsquo;re actually changing the &lt;code>device pixel ratio&lt;/code>. If we set it to 150%, the device pixel ratio becomes 1.5, meaning that text that appears as 12px in CSS will physically display as 18px, although the CSS still shows 12px. This explains why sometimes web pages look like they have larger fonts than others.&lt;/p>
&lt;p>We can also get the actual &lt;code>device pixel ratio&lt;/code> by executing a JavaScript script.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/qUM7eQy.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="how-to-solve">
&lt;a class="heading-anchor-link" href="#how-to-solve">How to Solve&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-solve"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With the problem identified, the solution is either to control the scrollbar widths to be consistent or to make the scrollbar width not count in calculations. Here, I directly set the scrollbar to not occupy space:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-small&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-content&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-header&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">overflow-y&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">overlay&lt;/span> &lt;span class="cp">!important&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-fixed-header&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-content&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-scroll&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">ant-table-body&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">overflow-y&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">overlay&lt;/span> &lt;span class="cp">!important&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note&lt;/em> that &lt;code>overlay&lt;/code> is only supported in &lt;code>WebKit and Blink&lt;/code> kernel browsers&lt;/p>
&lt;blockquote>
&lt;/blockquote>
&lt;p>Behaves the same as auto, but with the scrollbars drawn on top of content instead of taking up space. Only supported in WebKit-based (e.g., Safari) and Blink-based (e.g., Chrome or Opera) browsers.&lt;/p>
&lt;p>From MDN&lt;/p>
&lt;p>Also, the reason for adding !important here is that the scroll styles added by the antd component are inline styles, so this is necessary to make our solution work.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://support.microsoft.com/en-us/help/4027860/windows-10-view-display-settings" target="_blank" rel="noopener">View display settings in Windows 10&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/devicePixelRatio" target="_blank" rel="noopener">Window.devicePixelRatio&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://segmentfault.com/a/1190000017044563" target="_blank" rel="noopener">How to solve the problem of page width being compressed when scrollbar appears?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow" target="_blank" rel="noopener">CSS MDN&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Time Formatting in JavaScript</title><link>https://en.1991421.cn/2020/02/03/js-date-formatting/</link><pubDate>Mon, 03 Feb 2020 23:53:35 +0800</pubDate><guid>https://en.1991421.cn/2020/02/03/js-date-formatting/</guid><description>&lt;blockquote>
&lt;p>I recently needed custom time formatting in a web app. After solving it, I documented it here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="requirement-1-use-utc-zero-time-zone">
&lt;a class="heading-anchor-link" href="#requirement-1-use-utc-zero-time-zone">Requirement 1: Use UTC (zero time zone)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirement-1-use-utc-zero-time-zone"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To standardize time display, we needed to show time in UTC. Since we already use a shared date utility based on momentJS, we only needed to set the default time zone at web startup.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">moment&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tz&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setDefault&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Africa/Abidjan&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: &lt;code>Africa/Abidjan&lt;/code> is UTC.&lt;/p>
&lt;h2 id="requirement-2-display-format-based-on-system-locale-while-keeping-utc">
&lt;a class="heading-anchor-link" href="#requirement-2-display-format-based-on-system-locale-while-keeping-utc">Requirement 2: Display format based on system locale while keeping UTC&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="requirement-2-display-format-based-on-system-locale-while-keeping-utc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the locale is en-us:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">2/3/2020
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">February 3, 2020 at 3:18 PM
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the locale is en-gb:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">03/02/2020
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3 February 2020 at 15:17
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Because time zone must stay UTC, moment formatting alone is not ideal: you either stick to a fixed time zone or manually specify a format. Fortunately, JS has &lt;code>toLocaleDateString&lt;/code>.&lt;/p>
&lt;p>Here is the solution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="err">#&lt;/span> &lt;span class="nb">Date&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">moment&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toDate&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toLocaleDateString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">language&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">timeZone&lt;/span>: &lt;span class="kt">moment.defaultZone.name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">#&lt;/span> &lt;span class="nb">Date&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">moment&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toDate&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toLocaleDateString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">language&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">timeStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;short&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dateStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;long&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">timeZone&lt;/span>: &lt;span class="kt">moment.defaultZone.name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="browser-language">
&lt;a class="heading-anchor-link" href="#browser-language">Browser language&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-language"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since we rely on locale formatting, how do we test it? We need to understand browser language settings.&lt;/p>
&lt;ol>
&lt;li>Browsers default to the system language at install time.&lt;/li>
&lt;li>Browsers allow custom language settings, which may differ from the system. A web app should follow the browser language unless it has its own language logic. So you do not need to follow the OS language.&lt;/li>
&lt;li>For Chrome language settings, see &lt;a href="https://www.digitaltrends.com/web/how-to-change-your-language-in-google-chrome/" target="_blank" rel="noopener">here&lt;/a>.&lt;/li>
&lt;li>Change the first language as above, refresh the web page, and you can test the display.&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>It is a small problem, but worth recording.&lt;/p></description></item><item><title>redux-saga-promise</title><link>https://en.1991421.cn/2020/02/02/79db3a0/</link><pubDate>Sun, 02 Feb 2020 14:50:54 +0800</pubDate><guid>https://en.1991421.cn/2020/02/02/79db3a0/</guid><description>&lt;blockquote>
&lt;p>Redux-Saga lets an action trigger a rich sequence of synchronous and asynchronous work. Dispatching the action itself, however, is asynchronous.&lt;/p>
&lt;p>Sometimes we need to dispatch an action and wait for it to finish before doing something else. Two common strategies: (1) pass callbacks, which gets messy if you chain several actions and can lead to callback hell; (2) watch Redux state changes in the component to infer completion, which assumes every saga eventually writes to the store. What if we let sagas return values instead?&lt;/p>
&lt;p>Hence the question: can we make actions promise-based?&lt;/p>
&lt;/blockquote>
&lt;h2 id="redux-thunk">
&lt;a class="heading-anchor-link" href="#redux-thunk">redux-thunk&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-thunk"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If actions return Promises, redux-thunk naturally comes to mind. Thunk middleware executes a function, then dispatches the “real” action. But thunks quickly become unwieldy when you need sophisticated orchestration, which is why sagas exist.&lt;/p>
&lt;p>So thunk isn’t the tool for this specific problem.&lt;/p>
&lt;h2 id="redux-saga-promise">
&lt;a class="heading-anchor-link" href="#redux-saga-promise">redux-saga-promise&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-saga-promise"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I stumbled upon this library, skimmed the repo, and realized it fits the requirement nicely.&lt;/p>
&lt;h3 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The official docs and demo are clear, so I won’t rehash them. Here’s a reference commit: &lt;a href="https://github.com/alanhe421/react-demo/commit/52f7ef7a287a51407d00d89cc04f2cdf09ea8df8" target="_blank" rel="noopener">52f7ef7a287a51407d00d89cc04f2cdf09ea8df8&lt;/a>&lt;/p>
&lt;h4 id="notes">Notes&lt;/h4>&lt;ol>
&lt;li>
&lt;p>When registering listeners, attach the action creator itself—not just the action type—because the actual async action’s type name isn’t what you initially defined.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/YDQupne.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If the app throws &lt;code>regeneratorRuntime is not defined&lt;/code>, import &lt;code>regeneratorRuntime&lt;/code>. I covered the fix in another post on this blog.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you’ve used &lt;a href="https://github.com/dvajs/dva" target="_blank" rel="noopener">dva.js&lt;/a>, you know it wraps saga so actions already return Promises. That’s an easy path if you don’t want to wire things manually.&lt;/p>
&lt;p>The trade-off: the higher the level of abstraction, the less visibility you have into the internals. When issues arise, you may end up waiting on upstream fixes. I prefer lightweight stacks—assemble the architecture that fits your team and tech choices.&lt;/p></description></item><item><title>regeneratorRuntime is not defined</title><link>https://en.1991421.cn/2020/02/02/regeneratorruntime-is-not-defined/</link><pubDate>Sun, 02 Feb 2020 14:19:50 +0800</pubDate><guid>https://en.1991421.cn/2020/02/02/regeneratorruntime-is-not-defined/</guid><description>&lt;blockquote>
&lt;p>After introducing &lt;code>@adobe/redux-saga-promise&lt;/code>, the frontend refused to boot with a &lt;code>regeneratorRuntime is not defined&lt;/code> error. Here’s the investigation and fix.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/jmQxpPr.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-is-regenerator-runtime">
&lt;a class="heading-anchor-link" href="#what-is-regenerator-runtime">What Is regenerator-runtime?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-regenerator-runtime"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Source transformer enabling ECMAScript 6 generator functions in JavaScript-of-today.&lt;/p>
&lt;/blockquote>
&lt;p>In short, it polyfills generator and async function support for compiled JavaScript.&lt;/p>
&lt;p>Official docs: &lt;a href="https://github.com/facebook/regenerator/tree/master/packages/regenerator-runtime" target="_blank" rel="noopener">https://github.com/facebook/regenerator/tree/master/packages/regenerator-runtime&lt;/a>&lt;/p>
&lt;h2 id="checking-package-dependencies">
&lt;a class="heading-anchor-link" href="#checking-package-dependencies">Checking package dependencies&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="checking-package-dependencies"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;code>@adobe/redux-saga-promise&lt;/code> lists &lt;code>&amp;quot;regenerator-runtime&amp;quot;: &amp;quot;^0.13.3&amp;quot;&lt;/code> in devDependencies, so the error is tied to this addition.&lt;/li>
&lt;li>The package uses the runtime during tests but expects it to exist at runtime as well.&lt;/li>
&lt;/ul>
&lt;h2 id="typescript-configuration">
&lt;a class="heading-anchor-link" href="#typescript-configuration">TypeScript configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/pSVfw6G.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The project compiles to ES6. Generators and async/await are part of ES6, and the codebase already runs redux-saga without issues. Why does adding &lt;code>redux-saga-promise&lt;/code> crash?&lt;/p>
&lt;p>Looking at its source, the package directly references &lt;code>regeneratorRuntime&lt;/code>. The compiled output targets ES5, so &lt;code>regeneratorRuntime&lt;/code> is effectively a production dependency. Even with an ES6 target, you still need to include the runtime when using this package.&lt;/p>
&lt;h2 id="fixes">
&lt;a class="heading-anchor-link" href="#fixes">Fixes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fixes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="webpack">
&lt;a class="heading-anchor-link" href="#webpack">Webpack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nx">webpack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ProvidePlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">regeneratorRuntime&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;regenerator-runtime/runtime&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="jest">
&lt;a class="heading-anchor-link" href="#jest">Jest&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jest"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In your test setup file (e.g., &lt;code>enzyme-setup.ts&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="s1">&amp;#39;regenerator-runtime/runtime&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="bundle-impact">
&lt;a class="heading-anchor-link" href="#bundle-impact">Bundle impact&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="bundle-impact"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Negligible—the runtime is under 1 KB when compressed.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Identify the root cause first; the fix often follows naturally.&lt;/p></description></item><item><title>IDEA Plugin - NPM Package Version History</title><link>https://en.1991421.cn/2020/02/01/idea-npm/</link><pubDate>Sat, 01 Feb 2020 17:06:05 +0800</pubDate><guid>https://en.1991421.cn/2020/02/01/idea-npm/</guid><description>&lt;blockquote>
&lt;p>In IDEA or WebStorm, for package versions, we can only see the currently installed version or the latest version number, but there&amp;rsquo;s no built-in support for viewing historical versions. So I thought, why not create a plugin to enhance this functionality?&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-need-for-viewing-package-version-history">
&lt;a class="heading-anchor-link" href="#the-need-for-viewing-package-version-history">The Need for Viewing Package Version History&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-need-for-viewing-package-version-history"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Viewing package version history has never been a critical requirement, but it&amp;rsquo;s still needed.&lt;/p>
&lt;p>For example, when using webpack 4.x, I might want to know what versions exist beyond 4.x, because upgrades don&amp;rsquo;t always go to the latest version.&lt;/p>
&lt;p>Another example: for packages we publish to private repositories, sometimes we need to know how many versions currently exist on the private server. Due to various reasons, some versions might no longer exist, and we need to verify.&lt;/p>
&lt;p>And so on&amp;hellip; the need exists, so all that&amp;rsquo;s left is to solve it.&lt;/p>
&lt;h2 id="npm--yarn-commands-for-viewing-package-version-history">
&lt;a class="heading-anchor-link" href="#npm--yarn-commands-for-viewing-package-version-history">npm | yarn Commands for Viewing Package Version History&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm--yarn-commands-for-viewing-package-version-history"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>npm or yarn commands themselves support querying available package version history&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm view webpack versions --json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn info webpack versions
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="plugin-development-process-overview">
&lt;a class="heading-anchor-link" href="#plugin-development-process-overview">Plugin Development Process Overview&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugin-development-process-overview"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Based on the above commands, the plugin essentially just wraps a visual UI around these commands. Let&amp;rsquo;s get started.&lt;/p>
&lt;h3 id="general-workflow">
&lt;a class="heading-anchor-link" href="#general-workflow">General Workflow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="general-workflow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Get the package name where the user&amp;rsquo;s cursor is focused&lt;/li>
&lt;li>Execute the command to get package version history&lt;/li>
&lt;li>Display the results in a popup list&lt;/li>
&lt;/ol>
&lt;p>I won&amp;rsquo;t go into the detailed development process here, just describe a few key points&lt;/p>
&lt;h3 id="key-points">
&lt;a class="heading-anchor-link" href="#key-points">Key Points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="controlling-menu-display-and-availability-state">Controlling Menu Display and Availability State&lt;/h4>&lt;p>My requirement is that the menu should only appear when the user right-clicks in a package.json file.&lt;/p>
&lt;p>Method: Override the update method, Presentation provides methods to control this.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">update&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@NotNull&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AnActionEvent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">PsiFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">psiFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CommonDataKeys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">PSI_FILE&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">psiFile&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getVirtualFile&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getName&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Presentation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">presentation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getPresentation&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;package.json&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">presentation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setEnabledAndVisible&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">presentation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setEnabledAndVisible&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="getting-the-package-name">Getting the Package Name&lt;/h4>&lt;p>My requirement is to get the package name when the user right-clicks on the package name position&lt;/p>
&lt;p>Method: PsiTreeUtil provides methods to get the direct parent element based on an element, thus obtaining the content&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">PsiElement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Objects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">requireNonNull&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PsiManager&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">findFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="na">findElementAt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">PsiElement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">firstParent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PsiTreeUtil&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">findFirstParent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">element&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Condition&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">PsiElement&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">value&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PsiElement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">psiElement&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">assert&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">firstParent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">packageName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getPackageName&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">LeafPsiElement&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">firstParent&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">...&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@NotNull&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getPackageName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LeafPsiElement&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">firstParent&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">firstParent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getText&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">length&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="displaying-popup-at-cursor-position">Displaying Popup at Cursor Position&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">showPopup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">versions&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Editor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">editor&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">IPopupChooserBuilder&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">popupChooserBuilder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">JBPopupFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getInstance&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">createPopupChooserBuilder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">versions&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">popupChooserBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setTitle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">JBPopup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">popup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">popupChooserBuilder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">createPopup&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">popup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setMinimumSize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Dimension&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">80&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">popup&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">showInBestPositionFor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">editor&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="executing-commands">Executing Commands&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cmds&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ArrayList&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">cmds&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;npm&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">GeneralCommandLine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">generalCommandLine&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GeneralCommandLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cmds&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">generalCommandLine&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setCharset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Charset&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">forName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">generalCommandLine&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setWorkDirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getBasePath&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">generalCommandLine&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addParameters&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;view&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;versions&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;--json&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">commandLineOutputStr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ScriptRunnerUtil&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getProcessOutput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">generalCommandLine&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gson&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">converter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gson&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TypeToken&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}.&lt;/span>&lt;span class="na">getType&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">converter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">fromJson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">commandLineOutputStr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-result">
&lt;a class="heading-anchor-link" href="#final-result">Final Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/V9D3Hr5.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Plugin Store Address: &lt;a href="https://plugins.jetbrains.com/plugin/13748-view-package-versions" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>This should be the second IDEA plugin I&amp;rsquo;ve personally developed. The motivation was simple - existing IDEs and plugins don&amp;rsquo;t support this functionality, so I decided to develop it myself. Using plugins to fill the gaps in IDE capabilities helps improve personal productivity.&lt;/li>
&lt;li>The official documentation doesn&amp;rsquo;t provide examples for API methods, so relying solely on IDEA&amp;rsquo;s documentation isn&amp;rsquo;t sufficient. Multiple approaches are needed to solve problems during plugin development, such as posting on the official IDEA community forum, communicating with them on Slack, and examining the IDE and existing plugin SDKs. These can all help address current issues, so a diversified approach is necessary.&lt;/li>
&lt;/ol>
&lt;h2 id="reference-documents">
&lt;a class="heading-anchor-link" href="#reference-documents">Reference Documents&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documents"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://www.jetbrains.org/intellij/sdk/docs/welcome.html" target="_blank" rel="noopener">IntelliJ Platform SDK&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/JetBrains/intellij-community" target="_blank" rel="noopener">intellij-community&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/@andresdom/developing-an-intellij-webstorm-javascript-plugin-65416f9afea3" target="_blank" rel="noopener">Developing an IntelliJ / WebStorm JavaScript plugin&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/JetBrains/intellij-plugins" target="_blank" rel="noopener">intellij-plugins&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://plugins.jetbrains.com/slack" target="_blank" rel="noopener">Slack&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://intellij-support.jetbrains.com/hc/en-us/community/posts" target="_blank" rel="noopener">IDEs Support (IntelliJ Platform) | JetBrains&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.jetbrains.org/intellij/sdk/docs/appendix/resources/useful_links.html" target="_blank" rel="noopener">Official Recommended Resources&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Difference Between implements and extends in TypeScript</title><link>https://en.1991421.cn/2020/01/30/typescriptimplementsextends/</link><pubDate>Thu, 30 Jan 2020 20:37:33 +0800</pubDate><guid>https://en.1991421.cn/2020/01/30/typescriptimplementsextends/</guid><description>&lt;blockquote>
&lt;p>Understanding &lt;code>implements&lt;/code> vs. &lt;code>extends&lt;/code> helps you choose the right composition pattern in TypeScript.&lt;/p>
&lt;/blockquote>
&lt;h2 id="definitions">
&lt;a class="heading-anchor-link" href="#definitions">Definitions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="definitions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="implements">
&lt;a class="heading-anchor-link" href="#implements">&lt;code>implements&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>“Implement” a contract. A class implements an interface (or another class) by providing all required members. It can override methods/properties and add new ones.&lt;/p>
&lt;h3 id="extends">
&lt;a class="heading-anchor-link" href="#extends">&lt;code>extends&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extends"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>“Inherit” behavior. A class or interface extends another class/interface, inheriting members. Properties are inherited; methods can be overridden.&lt;/p>
&lt;h2 id="key-rules-in-typescript">
&lt;a class="heading-anchor-link" href="#key-rules-in-typescript">Key Rules in TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-rules-in-typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Only classes use &lt;code>implements&lt;/code> (classes can implement interfaces or classes).&lt;/li>
&lt;li>Interfaces can extend interfaces or classes.&lt;/li>
&lt;li>Classes can only extend classes (not interfaces).&lt;/li>
&lt;li>Multiple inheritance/implementation is allowed.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/IkoFHiq.png"
alt="diagram"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="java-comparison">
&lt;a class="heading-anchor-link" href="#java-comparison">Java Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="java-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In Java:&lt;/p>
&lt;ol>
&lt;li>Same as TypeScript: classes implement interfaces and extend classes. Multiple inheritance via interfaces is allowed.&lt;/li>
&lt;li>Difference: interfaces can’t extend classes—only other interfaces.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/ANFdCIZ.png"
alt="java diagram"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="takeaway">
&lt;a class="heading-anchor-link" href="#takeaway">Takeaway&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeaway"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This is just a starting point; real-world use comes from practice.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/38834625/whats-the-difference-between-extends-and-implements-in-typescript" target="_blank" rel="noopener">What&amp;rsquo;s the difference between &amp;rsquo;extends&amp;rsquo; and &amp;lsquo;implements&amp;rsquo; in TypeScript&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/10839131/implements-vs-extends-when-to-use-whats-the-difference/34826272#34826272" target="_blank" rel="noopener">Implements vs extends: When to use? What&amp;rsquo;s the difference?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1" target="_blank" rel="noopener">Object-oriented programming (Wikipedia)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ts.xcatliu.com/advanced/class-and-interfaces" target="_blank" rel="noopener">Classes &amp;amp; Interfaces in TypeScript&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Publishing Public Packages to npmjs</title><link>https://en.1991421.cn/2020/01/21/npmjs/</link><pubDate>Tue, 21 Jan 2020 23:34:41 +0800</pubDate><guid>https://en.1991421.cn/2020/01/21/npmjs/</guid><description>&lt;blockquote>
&lt;p>Previously used the company&amp;rsquo;s nexus to publish some company-level UI component libraries. Recently wanted to publish the TSRules I summarized in projects to the source, because I want more than just company projects to be able to use them, so decided to use npmjs hosting.&lt;/p>
&lt;p>Here I&amp;rsquo;ll document the publishing process.&lt;/p>
&lt;/blockquote>
&lt;h2 id="publishing-steps">
&lt;a class="heading-anchor-link" href="#publishing-steps">Publishing Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="publishing-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="npmjs-account-registration">
&lt;a class="heading-anchor-link" href="#npmjs-account-registration">npmjs Account Registration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npmjs-account-registration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Simple, skipping over&lt;/p>
&lt;h3 id="github-hosting">
&lt;a class="heading-anchor-link" href="#github-hosting">GitHub Hosting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="github-hosting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since updates and maintenance are needed, creating a corresponding repository is appropriate.&lt;/p>
&lt;p>&lt;a href="https://github.com/alanhe421/tslint-recommend-rule" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/dK7Pk6W.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="file-addition">
&lt;a class="heading-anchor-link" href="#file-addition">File Addition&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="file-addition"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since my project only hosts tslint.json, it&amp;rsquo;s relatively simple overall.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/NK8WsWX.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Initialize package.json using &lt;code>npm init&lt;/code>&lt;/li>
&lt;li>For package version management, it&amp;rsquo;s recommended to use commitizen, standard-version for semantic version control&lt;/li>
&lt;/ol>
&lt;h3 id="package-publishing">
&lt;a class="heading-anchor-link" href="#package-publishing">Package Publishing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-publishing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">npm adduser
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Why add access parameter? See below&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm publish --access&lt;span class="o">=&lt;/span>public
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Execute the above commands, and when the submission and publishing are successful, it&amp;rsquo;s done. Through the npmjs website, we can see the &lt;a href="https://www.npmjs.com/package/tslint-recommend-rule" target="_blank" rel="noopener">published package&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/1ezHDc8.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="precautions">
&lt;a class="heading-anchor-link" href="#precautions">Precautions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="precautions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="402-payment-required">
&lt;a class="heading-anchor-link" href="#402-payment-required">402 Payment Required&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="402-payment-required"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The reason for adding &lt;code>--access=public&lt;/code> here is that by default it publishes private packages, and NPM private repositories are paid. If you don&amp;rsquo;t add this parameter, you&amp;rsquo;ll encounter &lt;code>402 Payment Required&lt;/code>.&lt;/p>
&lt;h3 id="publish-file-whitelistblacklist">
&lt;a class="heading-anchor-link" href="#publish-file-whitelistblacklist">Publish File Whitelist/Blacklist&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="publish-file-whitelistblacklist"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When publishing packages, there&amp;rsquo;s often a need to only publish specific files, such as test files/compiled source files, etc. This requires specifying whitelist/blacklist.&lt;/p>
&lt;ol>
&lt;li>Use the files field in Package.json for whitelist&lt;/li>
&lt;li>Use .npmignore for blacklist&lt;/li>
&lt;li>Use .gitignore for blacklist&lt;/li>
&lt;/ol>
&lt;h4 id="notes-1">Notes&lt;/h4>&lt;ul>
&lt;li>
&lt;p>If files are explicitly specified, they have the highest priority&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If files are not specified but .npmignore exists, then exclude files based on it&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If .npmignore also doesn&amp;rsquo;t exist, then exclude based on .gitignore&lt;/p>
&lt;/li>
&lt;li>
&lt;p>npm defaults to excluding node_modules when publishing, so no manual exclusion is needed&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The reason for mentioning existence here is that empty files are also considered effective&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>package-json.lock&lt;/code> or &lt;code>yarn.lock&lt;/code> files are &lt;a href="https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json" target="_blank" rel="noopener">ignored&lt;/a> during publishing, so if the dependency package versions in this package don&amp;rsquo;t support modification, they need to be hardcoded. Lock files are only not ignored when used as top-level package version control, so lock files should still be submitted in regular projects - these two points are not contradictory.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Previously, every time rules were updated, I had to manually modify multiple libraries to achieve synchronization, which was purely tedious manual labor. This way, I only need to update the rule package version. Each project doesn&amp;rsquo;t need to directly maintain rule rules, and because there are versions, it also ensures the existence of differences. Cool!&lt;/p></description></item><item><title>Don't Use Yoda Conditions in Frontend</title><link>https://en.1991421.cn/2020/01/14/frontend-no-yoda-conditions/</link><pubDate>Tue, 14 Jan 2020 21:03:01 +0800</pubDate><guid>https://en.1991421.cn/2020/01/14/frontend-no-yoda-conditions/</guid><description>&lt;blockquote>
&lt;p>In frontend code, I was never settled on whether to put the constant on the left or the variable on the left in conditionals. Without a decision there’s no standard, and without a standard both styles coexist in the codebase, which feels messy. While searching around, I stumbled on the term &lt;code>Yoda conditions&lt;/code>—that seemed to hold the answer, hence this post.&lt;/p>
&lt;/blockquote>
&lt;h2 id="yoda-conditions">
&lt;a class="heading-anchor-link" href="#yoda-conditions">Yoda conditions?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="yoda-conditions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Yoda conditions are a programming style where the two sides of an expression in a conditional appear in the reverse of the typical order. The name comes from Jedi Master Yoda in Star Wars, who speaks with inverted word order. For example: “When nine hundred years old you reach, look as good you will not.”&lt;/p>
&lt;h3 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">if ( $value == 42 ) { /* ... */ }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// Reads like: &amp;#34;If the value is equal to 42...&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// Yoda condition
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">if ( 42 == $value ) { /* ... */ }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">// Reads like: &amp;#34;If 42 equals the value...&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now it’s clear—this is what “Yoda conditions” refers to.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/9oPH0h7.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="pros-and-cons-of-yoda-conditions">
&lt;a class="heading-anchor-link" href="#pros-and-cons-of-yoda-conditions">Pros and cons of Yoda conditions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pros-and-cons-of-yoda-conditions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="pros">
&lt;a class="heading-anchor-link" href="#pros">Pros&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pros"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>Accidental assignment gets caught&lt;/p>
&lt;p>For example, if we mistakenly write &lt;code>=&lt;/code> instead of &lt;code>===&lt;/code> in a conditional, &lt;code>value = 1&lt;/code> won’t throw, but &lt;code>1 = value&lt;/code> will fail immediately, making the mistake obvious.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Addresses unsafe null comparisons in Java&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="cons">
&lt;a class="heading-anchor-link" href="#cons">Cons&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cons"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Hurts readability&lt;/li>
&lt;/ul>
&lt;h3 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Regarding the first “pro,” even if putting the variable first can hide the mistake, such errors can and should be prevented by other means—lint rules or even code review. The second “pro” applies in languages like Java; in JavaScript, whether the variable is on the left or right doesn’t actually prevent null-related issues. The downside is clear.&lt;/p>
&lt;p>Overall, the drawbacks of Yoda conditions outweigh the benefits. My recommendation: don’t use Yoda conditions in frontend code.&lt;/p>
&lt;h2 id="lint">
&lt;a class="heading-anchor-link" href="#lint">Lint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Having reached a conclusion, we should enforce the preferred style. We typically add a linter to control code style, and Yoda-style comparisons have corresponding rules we can enable to control conditional expressions.&lt;/p>
&lt;h3 id="in-eslint">
&lt;a class="heading-anchor-link" href="#in-eslint">In ESLint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="in-eslint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#34;yoda&amp;#34;:&amp;#34;never&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>See docs: &lt;a href="https://eslint.org/docs/rules/yoda" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;h3 id="in-tslint">
&lt;a class="heading-anchor-link" href="#in-tslint">In TSLint&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="in-tslint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#34;binary-expression-operand-order&amp;#34;: true
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>See docs: &lt;a href="https://palantir.github.io/tslint/rules/binary-expression-operand-order/" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>It might seem that as long as a conditional works, the exact form doesn’t matter. No—details like Yoda conditions reflect thoughtful consideration of coding style by good engineers, and we should think about them too.&lt;/li>
&lt;li>It was precisely this pursuit of better conditional style that led me to learn about Yoda conditions. So, “sweating the details is never overkill.”&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.im/post/5af29ca751882567382f94c5" target="_blank" rel="noopener">Yoda Conditions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/wiki/%E5%B0%A4%E9%81%94%E6%A2%9D%E4%BB%B6%E5%BC%8F" target="_blank" rel="noopener">Yoda Conditions Wiki&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>NPM Indirect Dependency Version Issues</title><link>https://en.1991421.cn/2020/01/13/npm-indirect-dependency-version/</link><pubDate>Mon, 13 Jan 2020 23:07:02 +0800</pubDate><guid>https://en.1991421.cn/2020/01/13/npm-indirect-dependency-version/</guid><description>&lt;blockquote>
&lt;p>Package.json contains dependencies and devDependencies, which are collectively referred to as direct dependencies. However, the packages we depend on may themselves depend on other packages. How are the versions of these indirect dependencies managed? And when our project directly depends on A 1.x while an indirect package depends on A 2.x, which version will ultimately be installed - 1.x or 2.x?&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/oZRflfJ.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="the-answer">
&lt;a class="heading-anchor-link" href="#the-answer">The Answer&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-answer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>Actually, both versions will coexist&lt;/code>. The package.json records a dependency tree, and the storage structure under node_modules reflects this.&lt;/p>
&lt;p>For example, a project depends on &lt;code>@types/react&lt;/code>, while the dependency &lt;code>@types/react-router-dom&lt;/code> indirectly depends on &lt;code>@types/react&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/JobaOGw.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/hVzK25v.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="note">Note&lt;/h4>&lt;p>The &lt;code>*&lt;/code> here indicates any version, so the latest version will be installed during installation.&lt;/p>
&lt;h3 id="executing-yarn-install">
&lt;a class="heading-anchor-link" href="#executing-yarn-install">Executing yarn install&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="executing-yarn-install"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/x1nFysu.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/uALbGRS.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown above, the project actually installs two versions of &lt;code>@types/react&lt;/code>. One version &lt;code>16.8.25&lt;/code> is at the root of node_modules, while another version &lt;code>16.9.17&lt;/code> is under &lt;code>@types/react-router-dom&lt;/code>.&lt;/p>
&lt;h2 id="the-error">
&lt;a class="heading-anchor-link" href="#the-error">The Error&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-error"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When there are two versions of &lt;code>@types/react&lt;/code>, executing the build command results in an error:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/s70mYk4.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Remove the direct dependency on &lt;code>@types/react&lt;/code>, re-run the installation, and we&amp;rsquo;ll see a single &lt;code>16.9.17&lt;/code> version at the root of node_modules, with no &lt;code>@types/react&lt;/code> under &lt;code>@types/react-router-dom&lt;/code>. Re-running the build command no longer produces errors.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>When our package has both direct and indirect dependencies on different versions of the same package, both versions will actually be installed&lt;/li>
&lt;li>When direct and indirect dependencies require the same version, the package will be placed at the root path. When multiple versions exist, each package will be placed under the path of the package that depends on it&lt;/li>
&lt;/ol>
&lt;h2 id="extension">
&lt;a class="heading-anchor-link" href="#extension">Extension&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extension"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes in projects, we need to understand how a &lt;code>package@version&lt;/code> was introduced into the project. With layered dependencies, it&amp;rsquo;s not easy to directly check through lock files or node_modules. Fortunately, npm-cli provides query support:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> npm ls bignumber.js --package-lock-only
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-09-18-122028.jpeg"
alt="https://static.1991421.cn/2022/2022-09-18-122028.jpeg"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown in the image, we can see that &lt;a href="mailto:bignumber.js@9.0.0">bignumber.js@9.0.0&lt;/a> was installed into the project as a direct dependency of mysql.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/" target="_blank" rel="noopener">Understanding the npm dependency model&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/learnwithrahul/understanding-npm-dependency-resolution-84a24180901b" target="_blank" rel="noopener">Understanding npm dependency resolution&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Unknown vs. Any in TypeScript</title><link>https://en.1991421.cn/2020/01/12/typesciptunknownany/</link><pubDate>Sun, 12 Jan 2020 22:26:48 +0800</pubDate><guid>https://en.1991421.cn/2020/01/12/typesciptunknownany/</guid><description>&lt;blockquote>
&lt;p>For a full deep dive on &lt;code>unknown&lt;/code>, see the recommended article below. This post just clarifies the key difference between &lt;code>unknown&lt;/code> and &lt;code>any&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>any&lt;/code> is the top type but sacrifices safety—once you use it, the compiler stops helping. &lt;code>unknown&lt;/code> exists to fix that.&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>any&lt;/code> = no type safety. &lt;code>unknown&lt;/code> = must narrow before use.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/0JDoPAB.jpg"
alt="comparison 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/4rX44tk.jpg"
alt="comparison 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If the compiler can’t guarantee &lt;code>a&lt;/code> is a number, it won’t let you call &lt;code>.toString()&lt;/code> on &lt;code>unknown&lt;/code>. &lt;code>any&lt;/code> would allow it.&lt;/p>
&lt;p>With &lt;code>unknown&lt;/code>, you must perform your own type checks before manipulating the value.&lt;/p>
&lt;h2 id="type-guards">
&lt;a class="heading-anchor-link" href="#type-guards">Type Guards&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-guards"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/Q0UhWo5.jpg"
alt="type guard screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Notice the return type &lt;code>value is number&lt;/code> instead of &lt;code>boolean&lt;/code>. That tells TypeScript the check narrows the type:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/LLhSTz3.png"
alt="narrowing result"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="recommended-reading">
&lt;a class="heading-anchor-link" href="#recommended-reading">Recommended Reading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-reading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.im/post/5d04ac745188250a8b1fd203" target="_blank" rel="noopener">TypeScript 3.0: unknown type (translated)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Global Exception Interception and Customized Error Handling in Axios</title><link>https://en.1991421.cn/2019/12/29/axios/</link><pubDate>Sun, 29 Dec 2019 14:50:20 +0800</pubDate><guid>https://en.1991421.cn/2019/12/29/axios/</guid><description>&lt;blockquote>
&lt;p>In the current project, some common exceptions like 500, 400 need to be handled uniformly across the application, while for some request exceptions, like 400, we want specific functional blocks to handle them in a customized way. This creates a conflict, so I started looking for a solution.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-29-064701.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="partial-tech-stack">
&lt;a class="heading-anchor-link" href="#partial-tech-stack">Partial Tech Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="partial-tech-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="background">
&lt;a class="heading-anchor-link" href="#background">Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>We use Axios interceptors globally for implementation. The specific request code is generally handled in sagas, so customized exception handling also occurs there.&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>Redux-Saga&lt;/li>
&lt;li>Axios&lt;/li>
&lt;/ul>
&lt;h2 id="current-solution">
&lt;a class="heading-anchor-link" href="#current-solution">Current Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="current-solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="axios">
&lt;a class="heading-anchor-link" href="#axios">Axios&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="axios"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">axios&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;axios&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">onResponseSuccess&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">onResponseError&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">403&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">401&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;no auth&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">500&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">400&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;[axios-global]invalid request&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">Window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">Promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">interceptors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">onResponseSuccess&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onResponseError&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="saga-effects">
&lt;a class="heading-anchor-link" href="#saga-effects">Saga-effects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="saga-effects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">testExceptionEffects&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getBadRequest&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;saga exception&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="existing-issues">
&lt;a class="heading-anchor-link" href="#existing-issues">Existing Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="existing-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If &lt;code>getBadRequest&lt;/code> returns a 400 status, both places will handle the exception - both the Axios interceptor and the saga. And the execution order will be: axios global interceptor =&amp;gt; saga.&lt;/p>
&lt;h4 id="exception-messages">Exception Messages&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-111333.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="expectations">
&lt;a class="heading-anchor-link" href="#expectations">Expectations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="expectations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If we completely stop handling 400 errors globally, then each place would need to handle them individually, which is not my goal. I want global handling to still process common exceptions like 400, but also give certain requests the ability to handle exceptions in a customized way.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The execution order from Axios interceptors to saga exception catching cannot be modified, so what we can do is add a customization option in Axios to distinguish between customized and common exception handling strategies.&lt;/p>
&lt;h3 id="axios-1">
&lt;a class="heading-anchor-link" href="#axios-1">Axios&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="axios-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">onResponseError&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// axios config
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">403&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">401&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;no auth&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;[axios-global]server error&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">400&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">errorHandle&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">errorHandle&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;[axios-global]bad request&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">Window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">Promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="api">
&lt;a class="heading-anchor-link" href="#api">API&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="api"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">getBadRequest&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http://mock-api.com/wz2wL5gL.mock/test/bad-request&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">errorHandle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If we set &lt;code>errorHandle=true&lt;/code>, the interceptor will not process the 400 error and will directly throw it, allowing the saga to perform customized handling immediately afterward. This way, the conflict handling shown above won&amp;rsquo;t occur.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-29-064305.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If we still want certain requests to go through the global 400 exception handling, we can simply not add this configuration, and also not perform customized exception handling.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>The solution itself is relatively simple, but it addresses the need for personalized exception handling in specific business scenarios. It is more flexible compared to directly handling everything in a one-size-fits-all manner.&lt;/li>
&lt;li>Although the exception handling in sagas is described here, the same principle applies to exception handling within components.&lt;/li>
&lt;/ol></description></item><item><title>Redux-Saga Helper Functions</title><link>https://en.1991421.cn/2019/12/28/redux-saga/</link><pubDate>Sat, 28 Dec 2019 18:52:33 +0800</pubDate><guid>https://en.1991421.cn/2019/12/28/redux-saga/</guid><description>&lt;blockquote>
&lt;p>I’ve seen many teammates default to &lt;code>takeEvery&lt;/code>. That’s a mistake—pick the helper that matches the scenario. The official saga docs are thin, so I read the source, ran tests, and summarized the nuances here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-104500.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="helper-functions">
&lt;a class="heading-anchor-link" href="#helper-functions">Helper Functions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="helper-functions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="takeevery">
&lt;a class="heading-anchor-link" href="#takeevery">&lt;code>takeEvery&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeevery"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Official documentation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">takeEvery&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">allows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concurrent&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">actions&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">be&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handled&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">In&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">example&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">above&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">USER_REQUESTED&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dispatched&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">fetchUser&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">started&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">even&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">previous&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">fetchUser&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">still&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">pending&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">example&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">user&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clicks&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="k">Load&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">User&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">button&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">consecutive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">times&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rapid&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="n">nd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">click&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">will&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dispatch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">USER_REQUESTED&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">fetchUser&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fired&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">one&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hasn&lt;/span>&lt;span class="s1">&amp;#39;t yet terminated)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * `takeEvery` doesn&amp;#39;&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">out&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">of&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">responses&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">There&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">no&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">guarantee&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tasks&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">will&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">terminate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">same&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">they&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">were&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">started&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">To&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">out&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">of&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">responses&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">you&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">may&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">consider&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">takeLatest&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">below&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Notes&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;code>takeEvery&lt;/code> cannot guarantee that effects finish in the same order they were triggered.&lt;/li>
&lt;li>Every dispatched action spawns its own effect.&lt;/li>
&lt;/ol>
&lt;h3 id="takeleading">
&lt;a class="heading-anchor-link" href="#takeleading">&lt;code>takeLeading&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeleading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spawns&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">saga&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">each&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dispatched&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Store&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">matches&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">After&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">spawning&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">once&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">it&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">blocks&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">until&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">spawned&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">saga&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">completes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">then&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">starts&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">listen&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">again&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">In&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">short&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">takeLeading&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">listening&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">actions&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">it&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">doesn&lt;/span>&lt;span class="s1">&amp;#39;t run a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * saga.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Notes&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;code>takeLeading&lt;/code> only runs when there isn’t already a matching saga in flight. If an action arrives while another instance is still running, it’s ignored.&lt;/li>
&lt;/ol>
&lt;h3 id="takelatest">
&lt;a class="heading-anchor-link" href="#takelatest">&lt;code>takeLatest&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takelatest"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Spawns&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">saga&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">each&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">action&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dispatched&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Store&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">matches&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">And&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">automatically&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cancels&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">any&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">previous&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">saga&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">started&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">previously&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">it&lt;/span>&lt;span class="s1">&amp;#39;s still running.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * Each time an action is dispatched to the store. And if this action matches
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * `pattern`, `takeLatest` starts a new `saga` task in the background. If a
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * `saga` task was started previously (on the last action dispatched before the
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * actual action), and if this task is still running, the task will be
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> * cancelled.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Notes&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>When the action fires and an effect is still running, &lt;code>takeLatest&lt;/code> cancels the old effect and starts the new one.&lt;/li>
&lt;/ol>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">fetchUserEffects&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Run #&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">count&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">userInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUserInfo&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">setUserInfoAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userInfo&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">delay&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">userHistory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUserHistory&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">setUserHistory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userHistory&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>count&lt;/code> increments with each click; the first run starts at 1.&lt;/p>
&lt;h3 id="takeevery-1">
&lt;a class="heading-anchor-link" href="#takeevery-1">&lt;code>takeEvery&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeevery-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Using &lt;code>yield takeEvery('USER_FETCH', fetchUserEffects);&lt;/code> and dispatching &lt;code>USER_FETCH&lt;/code> four times:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-101807.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-101304.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-101125.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Each action spawns its own saga instance—four clicks produce four complete runs.&lt;/p>
&lt;h3 id="takeleading-1">
&lt;a class="heading-anchor-link" href="#takeleading-1">&lt;code>takeLeading&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeleading-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Switch to &lt;code>takeLeading&lt;/code> and fire the action four times. Only the first run executes:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-102605.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="takelatest-1">
&lt;a class="heading-anchor-link" href="#takelatest-1">&lt;code>takeLatest&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takelatest-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>With &lt;code>takeLatest&lt;/code>, dispatch the action four times. You’ll see four saga starts, but only the last one finishes fully:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-102820.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-102834.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-28-102852.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Why?&lt;/strong> The docs explain it: when a new action arrives while the previous saga is still running (for example, waiting at &lt;code>delay&lt;/code>), the previous saga is cancelled. The console shows all four &lt;code>userInfo&lt;/code> logs, but only the final &lt;code>userHistory&lt;/code> completes.&lt;/p>
&lt;p>&lt;strong>What about in-flight &lt;code>call&lt;/code>s?&lt;/strong> If an API request is already in progress when a new action arrives, &lt;code>takeLatest&lt;/code> does &lt;strong>not&lt;/strong> abort the network call. Cancellation means the generator stops processing further yielded values; pending operations finish on their own.&lt;/p>
&lt;h2 id="usage-tips">
&lt;a class="heading-anchor-link" href="#usage-tips">Usage Tips&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage-tips"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>For searches or queries where newer requests supersede older ones, use &lt;code>takeLatest&lt;/code>.&lt;/li>
&lt;li>For counters or workflows where every action must run, use &lt;code>takeEvery&lt;/code>.&lt;/li>
&lt;li>When the work is idempotent and repeating it is wasteful, &lt;code>takeLeading&lt;/code> avoids duplicate executions.&lt;/li>
&lt;/ul>
&lt;p>In short: choose based on the behavior you need.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://redux-saga-in-chinese.js.org/docs/basics/UsingSagaHelpers.html" target="_blank" rel="noopener">https://redux-saga-in-chinese.js.org/docs/basics/UsingSagaHelpers.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Duplicate identifier LibraryManagedAttributes</title><link>https://en.1991421.cn/2019/12/24/duplicate-identifier-librarymanagedattributes/</link><pubDate>Tue, 24 Dec 2019 13:43:47 +0800</pubDate><guid>https://en.1991421.cn/2019/12/24/duplicate-identifier-librarymanagedattributes/</guid><description>&lt;blockquote>
&lt;p>Recently, when upgrading a frontend package, I encountered the following error. After analysis, I finally fixed it. Here&amp;rsquo;s my note on this issue.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-24-034917.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="root-cause">
&lt;a class="heading-anchor-link" href="#root-cause">Root Cause&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="root-cause"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Looking at the specific error message, there are two versions of React type definitions existing simultaneously.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-24-053024.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-24-053111.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;code>@types/react&lt;/code> versions in yarn.lock - note there are two&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-24-053203.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="solutions">
&lt;a class="heading-anchor-link" href="#solutions">Solutions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h3 id="tsc-configuration-skiplibcheck-wrong-approach">
&lt;a class="heading-anchor-link" href="#tsc-configuration-skiplibcheck-wrong-approach">TSC Configuration skipLibCheck &lt;code>Wrong Approach&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tsc-configuration-skiplibcheck-wrong-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If you add the skipLibCheck configuration to tsconfig.json and recompile with TSC, it actually works, but this approach is not recommended because you&amp;rsquo;re giving up some type safety. So don&amp;rsquo;t do this, even though it&amp;rsquo;s possible.&lt;/p>
&lt;h3 id="packagejson-current-approach">
&lt;a class="heading-anchor-link" href="#packagejson-current-approach">Package.json &lt;code>Current Approach&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packagejson-current-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add the following configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;resolutions&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;~16.8.19&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Re-run the yarn command, and you&amp;rsquo;ll find that the lock file now only has one version.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-24-053955.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="other-methods">
&lt;a class="heading-anchor-link" href="#other-methods">Other Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Besides the above solution, we can manually or use &lt;a href="https://github.com/atlassian/yarn-deduplicate" target="_blank" rel="noopener">tools&lt;/a> to remove duplicate package issues, though the lock file will inevitably need modification.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/yarnpkg/yarn/issues/3967" target="_blank" rel="noopener">Yarn upgrade creates duplicate dependency resolution #3967
&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/atlassian/yarn-deduplicate" target="_blank" rel="noopener">yarn-deduplicate&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.typescriptlang.org/docs/handbook/compiler-options.html" target="_blank" rel="noopener">TypeScript Compiler Options&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://eggjs.org/zh-cn/tutorials/typescript.html" target="_blank" rel="noopener">https://eggjs.org/zh-cn/tutorials/typescript.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://yarnpkg.com/en/docs/selective-version-resolutions" target="_blank" rel="noopener">yarn selective-version-resolutions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://segmentfault.com/a/1190000017075256" target="_blank" rel="noopener">Differences between package-lock.json and yarn.lock dependencies&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Read-only Nature of React Props</title><link>https://en.1991421.cn/2019/12/22/react-props/</link><pubDate>Sun, 22 Dec 2019 21:20:49 +0800</pubDate><guid>https://en.1991421.cn/2019/12/22/react-props/</guid><description>&lt;blockquote>
&lt;p>When developing React components, Props cannot be modified. But why? How to ensure they aren&amp;rsquo;t modified in actual development as much as possible? Let&amp;rsquo;s continue exploring.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-22-140603.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="explanation-of-props-in-react">
&lt;a class="heading-anchor-link" href="#explanation-of-props-in-react">Explanation of Props in React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation-of-props-in-react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Whether you declare a component as a function or a class, it must never modify its own props.
React is pretty flexible but it has a single strict rule:
All React components must act like pure functions with respect to their props.&lt;/p>
&lt;/blockquote>
&lt;h3 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>The React library doesn&amp;rsquo;t enforce that props cannot be modified, but the rule is that they shouldn&amp;rsquo;t be modified - yes, this is a &lt;code>Rule&lt;/code>&lt;/li>
&lt;li>Neither function components nor class components should modify Props to avoid side effects&lt;/li>
&lt;/ol>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// App renders a user.name and a profile
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">App&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;div&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">props&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Profile&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">props&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Profile changes the user.name and renders it
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Now App has the wrong DOM.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">Profile&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Voldemort&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Uh oh!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;div&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Render the App and give it props
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">ReactDOM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">App&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Hermione&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;app&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-22-134200.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown above, although we modified user.name in the child component, the parent component still prints Hermione. Why? Because props is an object, and the object itself wasn&amp;rsquo;t modified - React component updates use shallow comparison.&lt;/p>
&lt;h2 id="react-component-typescript-definition">
&lt;a class="heading-anchor-link" href="#react-component-typescript-definition">React Component TypeScript Definition&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-component-typescript-definition"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">class&lt;/span> &lt;span class="nx">Component&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">P&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">S&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">props&lt;/span>: &lt;span class="kt">Readonly&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">P&lt;/span>&lt;span class="p">&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As can be seen above, Props are read-only parameters, which ensures a certain level of safety. If we try to assign directly, TSC will report an error&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-22-134753.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>So is that all good? No, what if the parameter is an object type?&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-22-134839.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown above, it won&amp;rsquo;t report an error, but it&amp;rsquo;s a BUG. The fundamental problem is that the current Type definition cannot ensure that nested properties of properties are read-only. Regarding this, the community has already discussed and proposed solutions, but the new definitions haven&amp;rsquo;t been merged in yet.&lt;/p>
&lt;p>Discussion &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped/pull/35210" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>How can we ensure absolute immutability currently? You can manually add the new definitions to your project.&lt;/p>
&lt;h2 id="select-in-redux-saga">
&lt;a class="heading-anchor-link" href="#select-in-redux-saga">Select in Redux Saga&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="select-in-redux-saga"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In components, we use Redux values as Props, so they should still be used read-only. Another usage scenario is in effects using &lt;code>const state = yield select();&lt;/code> - should this state also be read-only?&lt;/p>
&lt;p>YES, select actually calls store.get(key) for a particular state. Note that it&amp;rsquo;s actually an object, so if we modify this object in effects, we&amp;rsquo;re actually modifying the single source of truth state tree. Therefore, &lt;code>when we get state through select, we must maintain read-only usage&lt;/code>&lt;/p>
&lt;h2 id="props-in-angular-and-vue">
&lt;a class="heading-anchor-link" href="#props-in-angular-and-vue">Props in Angular and Vue?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="props-in-angular-and-vue"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Angular and Vue don&amp;rsquo;t explicitly state that parameters passed from parent components cannot be modified, but note that they are components in usage, so it&amp;rsquo;s recommended not to modify them either.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding these fundamental design rules and concepts is beneficial for using these technologies and also helps with design patterns.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://reactjs.org/docs/components-and-props.html" target="_blank" rel="noopener">React Components and Props&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/26089532/why-cant-i-update-props-in-react-js" target="_blank" rel="noopener">Why can&amp;rsquo;t I update props in react.js?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/haizlin/fe-interview/issues/924" target="_blank" rel="noopener">Why React Props are Read-Only&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Talking About the Axios HTTP Library</title><link>https://en.1991421.cn/2019/12/14/httpaxios/</link><pubDate>Sat, 14 Dec 2019 12:55:04 +0800</pubDate><guid>https://en.1991421.cn/2019/12/14/httpaxios/</guid><description>&lt;blockquote>
&lt;p>Axios is the go-to HTTP library in many front-end stacks. Here are a few nuances I’ve run into.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-14-045615.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="axios-in-context">
&lt;a class="heading-anchor-link" href="#axios-in-context">Axios in context&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="axios-in-context"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I like understanding a tool’s mission so I know its limits. Axios wraps &lt;code>XMLHttpRequest&lt;/code> in the browser (and &lt;code>http&lt;/code>/&lt;code>https&lt;/code> in Node) to offer a friendlier API—nothing more, nothing less.&lt;/p>
&lt;p>Official tagline: &lt;em>Promise based HTTP client for the browser and node.js&lt;/em>&lt;/p>
&lt;h2 id="questions-ive-bumped-into">
&lt;a class="heading-anchor-link" href="#questions-ive-bumped-into">Questions I’ve bumped into&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="questions-ive-bumped-into"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="which-status-codes-trigger-catch">
&lt;a class="heading-anchor-link" href="#which-status-codes-trigger-catch">Which status codes trigger &lt;code>catch&lt;/code>?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="which-status-codes-trigger-catch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Axios resolves the promise for &lt;code>200 &amp;lt;= status &amp;lt; 300&lt;/code>; everything else rejects.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-15-084119.png"
alt="Axios source screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-15-084249.png"
alt="Axios source screenshot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>100-level codes aren’t common. 300-level redirects are handled transparently by the browser. Practically, you’ll see rejections for 4xx and 5xx responses.&lt;/p>
&lt;h4 id="what-about-xhronerror">What about &lt;code>xhr.onerror&lt;/code>?&lt;/h4>&lt;p>Axios wraps XHR, so let’s peek at the API. XHR exposes &lt;code>onload&lt;/code> and &lt;code>onerror&lt;/code>. Does a 500 trigger &lt;code>onerror&lt;/code>? Nope. &lt;code>onerror&lt;/code> only fires on network-layer problems (e.g., the request couldn’t be sent). 4xx and 5xx still hit &lt;code>onload&lt;/code>. So Axios rejections stem from application-level status checks, not XHR &lt;code>onerror&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-15-095018.png"
alt="XHR docs"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="does-a-301-redirect-refresh-the-whole-page">
&lt;a class="heading-anchor-link" href="#does-a-301-redirect-refresh-the-whole-page">Does a 301 redirect refresh the whole page?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="does-a-301-redirect-refresh-the-whole-page"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>No. XHR exists to avoid full page reloads. The browser follows the redirect behind the scenes and returns the final response to your code.&lt;/p>
&lt;h3 id="sending-array-parameters">
&lt;a class="heading-anchor-link" href="#sending-array-parameters">Sending array parameters&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sending-array-parameters"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>One approach:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">defaults&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">paramsSerializer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">params&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">qs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">arrayFormat&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;repeat&amp;#39;&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This yields URLs like &lt;code>https://bugs.chromium.org/p/chromium/issues/detail?id=1&amp;amp;id=2&amp;amp;id=3&lt;/code>. Repeated keys are perfectly valid.&lt;/p>
&lt;h3 id="why-do-some-requests-show-canceled-in-devtools">
&lt;a class="heading-anchor-link" href="#why-do-some-requests-show-canceled-in-devtools">Why do some requests show &lt;code>canceled&lt;/code> in DevTools?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-do-some-requests-show-canceled-in-devtools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>That status means the front end aborted the request. XHR exposes &lt;code>abort()&lt;/code>:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-19-144102.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">The XMLHttpRequest.abort() method aborts the request if it has already been sent. When a request is aborted, its readyState is changed to XMLHttpRequest.UNSENT (0) and the request&amp;#39;s status code is set to 0.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Axios calls &lt;code>abort()&lt;/code> when a request exceeds the configured timeout, so DevTools lists it as &lt;code>canceled&lt;/code>. Contrast that with a 504 gateway timeout, which comes from the server.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-19-143757.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Nothing exotic here, but they’re easy details to forget—worth writing down.&lt;/p></description></item><item><title>Improve Frontend Code Quality with Tools — Version Automation</title><link>https://en.1991421.cn/2019/12/08/improve-code-quality-version-automation/</link><pubDate>Sun, 08 Dec 2019 22:16:13 +0800</pubDate><guid>https://en.1991421.cn/2019/12/08/improve-code-quality-version-automation/</guid><description>&lt;blockquote>
&lt;p>I’ve been building an internal UI component library to standardize product UI and improve delivery speed instead of reinventing components each time. Great goal, but it exposed a few release headaches.&lt;/p>
&lt;p>Libraries evolve and need regular publishing. Our current flow is: change code, commit, bump the version manually, then run the publish command. That causes several issues.&lt;/p>
&lt;/blockquote>
&lt;h2 id="pain-points">
&lt;a class="heading-anchor-link" href="#pain-points">Pain Points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pain-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>A change might be a &lt;code>fix&lt;/code>, a &lt;code>feat&lt;/code>, or even a &lt;code>breaking change&lt;/code>. Raw commit messages rarely capture that nuance, and we produce no public release notes. Consumers just see a new version number and feel anxious about upgrading.&lt;/li>
&lt;li>Manual version bumps are error-prone and inconsistent. A newcomer might mark a tiny CSS fix as a major release. Manual editing is also tedious.&lt;/li>
&lt;li>This isn’t limited to UI libraries — app repositories also deserve clear commits. Our internal history has plenty of throwaway messages with little value.&lt;/li>
&lt;li>Without standardized commits, every push can trigger CI and even a release pipeline. Documentation-only changes shouldn’t do that. The fix is to enforce structured, conventional commit messages.&lt;/li>
&lt;/ol>
&lt;p>Time to lean on tooling. Fortunately, the ecosystem is mature.&lt;/p>
&lt;h2 id="angular-as-a-reference">
&lt;a class="heading-anchor-link" href="#angular-as-a-reference">Angular as a Reference&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="angular-as-a-reference"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Angular’s commit history and changelog are exemplary. How do they do it?
&lt;a href="https://github.com/angular/angular/commits/master" target="_blank" rel="noopener">https://github.com/angular/angular/commits/master&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-03-024338.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-03-024804.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="semantic-versioning-refresher">
&lt;a class="heading-anchor-link" href="#semantic-versioning-refresher">Semantic Versioning Refresher&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="semantic-versioning-refresher"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Before wiring up the tools, revisit SemVer so version bumps stay meaningful.&lt;/p>
&lt;p>&lt;code>MAJOR.MINOR.PATCH&lt;/code> with the rules:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>MAJOR&lt;/strong>: incompatible API changes.&lt;/li>
&lt;li>&lt;strong>MINOR&lt;/strong>: backwards-compatible feature additions.&lt;/li>
&lt;li>&lt;strong>PATCH&lt;/strong>: backwards-compatible bug fixes.&lt;/li>
&lt;/ul>
&lt;p>Pre-release identifiers and build metadata can be appended. Full spec: &lt;a href="https://semver.org/" target="_blank" rel="noopener">semver.org&lt;/a>&lt;/p>
&lt;h2 id="setup">
&lt;a class="heading-anchor-link" href="#setup">Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="install-the-tooling">
&lt;a class="heading-anchor-link" href="#install-the-tooling">Install the Tooling&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-the-tooling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">yarn add -D commitizen &lt;span class="c1"># commit helper&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn add -D commitlint &lt;span class="c1"># lint commit messages&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn add -D cz-conventional-changelog &lt;span class="c1"># Angular-style prompts&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn add -D standard-version &lt;span class="c1"># SemVer + changelog automation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yarn add -D @commitlint/config-conventional
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="ide-plugin">
&lt;a class="heading-anchor-link" href="#ide-plugin">IDE Plugin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ide-plugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;a href="https://plugins.jetbrains.com/plugin/9861-git-commit-template" target="_blank" rel="noopener">Git Commit Template&lt;/a>&lt;/p>
&lt;ul>
&lt;li>We use JetBrains IDEs; find equivalents for other editors.&lt;/li>
&lt;li>The GUI makes structured commits friendlier than the terminal prompt.&lt;/li>
&lt;/ul>
&lt;h3 id="packagejson">
&lt;a class="heading-anchor-link" href="#packagejson">&lt;code>package.json&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;commitmsg&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;commitlint -e $GIT_PARAMS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;release&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;standard-version&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;config&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;commitizen&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./node_modules/cz-conventional-changelog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="commitlintconfigjs">
&lt;a class="heading-anchor-link" href="#commitlintconfigjs">&lt;code>commitlint.config.js&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="commitlintconfigjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">extends&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;@commitlint/config-conventional&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rules&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;subject-case&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;never&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;upper-case&amp;#34;&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="huskyrcjson">
&lt;a class="heading-anchor-link" href="#huskyrcjson">&lt;code>.huskyrc.json&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="huskyrcjson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hooks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;commit-msg&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;commitlint -e $GIT_PARAMS&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="versionrcjson">
&lt;a class="heading-anchor-link" href="#versionrcjson">&lt;code>.versionrc.json&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="versionrcjson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Example config: &lt;a href="https://github.com/alanhe421/jhipster-starter/blob/master/.versionrc.json" target="_blank" rel="noopener">alanhe421/jhipster-starter&lt;/a>&lt;/p>
&lt;h4 id="common-commit-types">Common Commit Types&lt;/h4>&lt;p>&lt;code>type&lt;/code> describes the scope of change. Frequently used values:&lt;/p>
&lt;ul>
&lt;li>&lt;code>feat&lt;/code>: new feature (maps to MINOR in SemVer)&lt;/li>
&lt;li>&lt;code>fix&lt;/code>: bug fix (maps to PATCH)&lt;/li>
&lt;li>&lt;code>to&lt;/code>: tweak that moves toward a fix but isn’t final; useful for multi-commit fixes before the concluding &lt;code>fix&lt;/code>&lt;/li>
&lt;li>&lt;code>docs&lt;/code>: documentation changes&lt;/li>
&lt;li>&lt;code>style&lt;/code>: formatting, missing semicolons; no production code changes&lt;/li>
&lt;li>&lt;code>refactor&lt;/code>: code restructure without new features or fixes&lt;/li>
&lt;li>&lt;code>perf&lt;/code>: performance improvements&lt;/li>
&lt;li>&lt;code>test&lt;/code>: add/refactor tests; no production code changes&lt;/li>
&lt;li>&lt;code>chore&lt;/code>: tooling, build scripts, maintenance; no production code changes&lt;/li>
&lt;li>&lt;code>revert&lt;/code>: rollback a commit&lt;/li>
&lt;li>&lt;code>build&lt;/code>: build system changes&lt;/li>
&lt;li>&lt;code>ci&lt;/code>: CI-related changes such as hooks or Docker configs&lt;/li>
&lt;/ul>
&lt;p>Full spec: &lt;a href="https://github.com/conventional-changelog/conventional-changelog-config-spec/blob/master/versions/2.1.0/README.md" target="_blank" rel="noopener">Conventional Changelog Config&lt;/a>&lt;/p>
&lt;p>The &lt;code>to&lt;/code> type comes from an Alibaba practice described &lt;a href="https://mp.weixin.qq.com/s/vzgST0ko-HZVkFFiSZ2xGg" target="_blank" rel="noopener">here&lt;/a>. It lets multiple incremental commits link to one eventual fix; the changelog groups them so you don’t get duplicates when &lt;code>to&lt;/code> entries are marked as &lt;code>hidden: false&lt;/code>.&lt;/p>
&lt;h2 id="workflow">
&lt;a class="heading-anchor-link" href="#workflow">Workflow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="workflow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="making-a-commit">
&lt;a class="heading-anchor-link" href="#making-a-commit">Making a Commit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="making-a-commit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>Use the IDE commit panel and enable the commit template. Fill out each field according to the prompts.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-04-205057.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>When it’s time to publish, run &lt;code>npm run release&lt;/code>. The script updates &lt;code>CHANGELOG.md&lt;/code>, bumps the version, and creates a commit automatically.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-15-134522.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>First run: &lt;code>npm run release -- --first-release&lt;/code>&lt;/li>
&lt;li>Prefer triggering releases in CI.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>Commits that don’t match the format fail immediately thanks to Git hooks.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-03-030628.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Because enforcement happens via local hooks, someone could disable &lt;code>.huskyrc.json&lt;/code> and bypass checks. If that becomes a problem, add server-side hooks.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="pre-release-versions">
&lt;a class="heading-anchor-link" href="#pre-release-versions">Pre-release Versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pre-release-versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Sometimes you need a fresh version for testing without blocking ongoing development. Use the pre-release flag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># &amp;#34;alpha&amp;#34; can also be &amp;#34;beta&amp;#34; or &amp;#34;rc&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm run release -- --prerelease alpha
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="alpha-beta-rc-refresher">Alpha, Beta, RC Refresher&lt;/h4>&lt;ul>
&lt;li>&lt;strong>alpha&lt;/strong>: internal test builds with rough edges and incomplete features; meant for developers/testers.&lt;/li>
&lt;li>&lt;strong>beta&lt;/strong>: public test builds; more stable than alpha but still experimental.&lt;/li>
&lt;li>&lt;strong>rc&lt;/strong>: release candidate; feature complete and essentially what will ship.&lt;/li>
&lt;li>&lt;strong>stable&lt;/strong>: the final GA release.&lt;/li>
&lt;/ul>
&lt;p>Ordering example: &lt;code>1.0.0-alpha &amp;lt; 1.0.0-alpha.1 &amp;lt; 1.0.0-alpha.beta &amp;lt; 1.0.0-beta &amp;lt; 1.0.0-beta.2 &amp;lt; 1.0.0-beta.11 &amp;lt; 1.0.0-rc.1 &amp;lt; 1.0.0&lt;/code>.&lt;/p>
&lt;p>Keep the process pragmatic for small projects.&lt;/p>
&lt;h3 id="troubleshooting">
&lt;a class="heading-anchor-link" href="#troubleshooting">Troubleshooting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="troubleshooting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Duplicate changelog entries&lt;/strong>&lt;/p>
&lt;p>I once saw repeated sections across releases. It was an upstream bug; reinstalling/upgrading the package fixed it. Keep your release tooling current.&lt;/p>
&lt;p>&lt;a href="https://github.com/conventional-changelog/conventional-changelog/issues/567" target="_blank" rel="noopener">Upstream issue&lt;/a>&lt;/p>
&lt;p>&lt;code>standard-version&lt;/code> derives the next release from two data points: the version in &lt;code>package.json&lt;/code> and the commits since the latest tag (&lt;code>git describe --tags --abbrev=0&lt;/code>).&lt;/p>
&lt;p>For example, if you delete local tags &lt;code>v0.1.14&lt;/code> and &lt;code>v0.1.13&lt;/code> and run &lt;code>npm run release&lt;/code>, the new version includes commits from both tags, so the changelog shows both &lt;code>feat&lt;/code> and &lt;code>build&lt;/code> entries.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-04-210155.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-01-04-210400.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Bypassing hooks&lt;/strong>&lt;/p>
&lt;p>Git hooks rely on shell scripts. If developers deliberately disable them, commits still pass locally. Server-side hooks are the only airtight safeguard.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Well-structured commit messages matter; compare Angular’s history with a typical repo and the gap is stark.&lt;/li>
&lt;li>Quality history helps new contributors ramp up and makes defect tracking easier.&lt;/li>
&lt;li>Enforcing conventions has a cost, but the benefits outweigh it.&lt;/li>
&lt;li>Good habits pay dividends for years—embrace them.&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://semver.org/" target="_blank" rel="noopener">Semantic Versioning 2.0.0&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/pigcan/blog/issues/15" target="_blank" rel="noopener">Use tooling to standardize git commit messages&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/XXHolic/blog/issues/16" target="_blank" rel="noopener">Git commit guidelines&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html" target="_blank" rel="noopener">Commit message and changelog guide&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/tunaiku-tech/automate-javascript-project-versioning-with-commitizen-and-standard-version-6a967afae7" target="_blank" rel="noopener">Automate JavaScript project versioning with commitizen and standard-version&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.crimx.com/2018/09/24/be-a-commitizen/" target="_blank" rel="noopener">How to submit code like a civilized person&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.ttlsa.com/linux/alpha-beta-rc/" target="_blank" rel="noopener">Alpha vs. beta vs. RC&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Publishing Frontend Packages to Private npm Registry</title><link>https://en.1991421.cn/2019/11/17/68614738/</link><pubDate>Sun, 17 Nov 2019 18:34:18 +0800</pubDate><guid>https://en.1991421.cn/2019/11/17/68614738/</guid><description>&lt;blockquote>
&lt;p>Previously, our team shared frontend base module resources using &lt;code>Git Submodule&lt;/code>, but there were drawbacks. So I spent the weekend researching Nexus npm to switch frontend resources to npm-based maintenance and management.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-11-17-102918.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="drawbacks-of-git-submodule">
&lt;a class="heading-anchor-link" href="#drawbacks-of-git-submodule">Drawbacks of Git Submodule&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="drawbacks-of-git-submodule"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>First, let me talk about the drawbacks of using Git Submodule for sharing frontend packages.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>Version management depends on commitId. Although submodule has the concept of branches, it essentially depends on commitID. The file storage itself is based on a certain repository and a certain commit. When doing MR code review, if versions are inconsistent, you can&amp;rsquo;t tell which version is newer just from the hash value. Compared to npm package&amp;rsquo;s major/minor version numbers, it&amp;rsquo;s much clearer and easier to understand.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The submodule management approach pulls down all module code, including UT (unit tests), which is actually redundant.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Submodule code in the project is not read-only and can be easily modified. I want code protection with only read permissions.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>So, I decided to switch to npm package management. Since our company has deployed a nexus private registry, let me introduce how to publish packages.&lt;/p>
&lt;h2 id="nexus-private-registry-deployment">
&lt;a class="heading-anchor-link" href="#nexus-private-registry-deployment">Nexus Private Registry Deployment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nexus-private-registry-deployment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since I haven&amp;rsquo;t learned about Nexus before, let me deploy it locally:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">docker run -d --restart=unless-stopped --name nexus -p 8081:8081 -p 5000:5000 -p 5001:5001 -p 5002:5002 -p 5003:5003 -p 5004:5004 --ulimit nofile=90000:90000 -e INSTALL4J_ADD_VM_PARAMS=&amp;#34;-Xms2g -Xmx2g&amp;#34; -v /nexus-data:/nexus-data sonatype/nexus3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-11-17-101732.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>Add three repositories: hosted, proxy, group. In the group repository, drag hosted and proxy into it.&lt;/p>
&lt;ul>
&lt;li>group refers to repository group, which can include hosted and proxy repositories.&lt;/li>
&lt;li>hosted refers to your own private repository where you can upload private code.&lt;/li>
&lt;li>proxy refers to proxy mirror repository, like the third-party libraries we commonly use such as antd, angular, etc.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-11-17-101653.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-11-17-101807.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Add npm Bearer Token Realm in Realms&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="frontend-package-configuration">
&lt;a class="heading-anchor-link" href="#frontend-package-configuration">Frontend Package Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-package-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="packagejson">
&lt;a class="heading-anchor-link" href="#packagejson">package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> &amp;#34;publishConfig&amp;#34;: {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;registry&amp;#34;: &amp;#34;http://localhost:8081/repository/npm-company/&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> },
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the repository is npm-company, which is the hosted repository&lt;/p>
&lt;h3 id="npmrc">
&lt;a class="heading-anchor-link" href="#npmrc">.npmrc&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npmrc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># auth config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">_auth=YWRtaW46YWRtaW4xMjM
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">always-auth=true
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">email=hi@1991421.cn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: _auth is the &lt;code>base64 encoded result of user:password&lt;/code>. This configuration solves password-free publishing, but it&amp;rsquo;s not the best solution because allowing everyone to publish is dangerous. A better approach is to have the CI server automatically publish after code merge or manually trigger publishing.&lt;/p>
&lt;p>&lt;a href="https://tool.chinaz.com/tools/base64.aspx" target="_blank" rel="noopener">Online Base64 Editor&lt;/a>&lt;/p>
&lt;h3 id="publishing">
&lt;a class="heading-anchor-link" href="#publishing">Publishing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="publishing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ npm publish
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-11-17-102742.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="package-usage">
&lt;a class="heading-anchor-link" href="#package-usage">Package Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In the specific development project&amp;rsquo;s .npmrc file, add the following configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">registry=http://localhost:8081/repository/npm/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the repository is npm, which is the group repository&lt;/p>
&lt;h2 id="common-errors">
&lt;a class="heading-anchor-link" href="#common-errors">Common Errors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="unable-to-authenticate-need-basic-realmsonatype-nexus-repository-manager">
&lt;a class="heading-anchor-link" href="#unable-to-authenticate-need-basic-realmsonatype-nexus-repository-manager">Unable to authenticate, need: BASIC realm=&amp;ldquo;Sonatype Nexus Repository Manager&amp;rdquo;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="unable-to-authenticate-need-basic-realmsonatype-nexus-repository-manager"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm ERR! code E401
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm ERR! Unable to authenticate, need: BASIC realm=&amp;#34;Sonatype Nexus Repository Manager&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When this error occurs, it&amp;rsquo;s usually because the registry is wrong. Pay attention to the registry mentioned above.&lt;/p>
&lt;p>Execute &lt;code>npm i alanhg-demo@0.3.0 --save&lt;/code>&lt;/p>
&lt;h3 id="401-or-404">
&lt;a class="heading-anchor-link" href="#401-or-404">401 or 404&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="401-or-404"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If you switch npmrc to yarnrc and execute yarn install to install packages, private packages on nexus will fail to install with 401 or 404 errors. The reason should be compatibility issues between yarn and nexus private registry. The current solution is to use npmrc to manage private registry sources.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/5yCXF6P.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="failed-to-stream-response-due-to-missing-blob-and-no-handler-set-to-recover">
&lt;a class="heading-anchor-link" href="#failed-to-stream-response-due-to-missing-blob-and-no-handler-set-to-recover">Failed to stream response due to: Missing blob and no handler set to recover.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="failed-to-stream-response-due-to-missing-blob-and-no-handler-set-to-recover"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Recently when executing package installation commands, the following error occurred:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">yarn install v1.22.4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[1/5] 🔍 Validating package.json...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[2/5] 🔍 Resolving packages...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">error An unexpected error occurred: &amp;#34;https://nexus.xxxcn/repository/npm/@alanhg%2fui: Failed to stream response due to: Missing blob and no handler set to recover.&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">info If you think this is a bug, please open a bug report with the information provided in &amp;#34;/Users/qhe/Documents/GitLab/xxx/yarn-error.log&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this ⌘.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The solution is to log into the nexus private registry and click invalidate cache.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2020/2020-04-08-235616.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-words">
&lt;a class="heading-anchor-link" href="#final-words">Final Words&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-words"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This only sets up the mechanism and solves usage and maintenance issues, but another important issue is package version evolution and package history. If we still use unchanging version numbers or very random version number changes, and each change doesn&amp;rsquo;t have systematic documentation and records, then it&amp;rsquo;s still a mess and we still can&amp;rsquo;t safely and confidently use packages. So we still need to use other tools to manage commit information and project versions properly. Sigh, solving one problem leads to discovering new problems. Don&amp;rsquo;t be afraid, keep going!&lt;/p>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://my.oschina.net/dkvirus/blog/1526525" target="_blank" rel="noopener">npm Package Publishing Pitfall Guide&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Transforming VSC into a Tool to Complement IDEA</title><link>https://en.1991421.cn/2019/10/27/vscidea/</link><pubDate>Sun, 27 Oct 2019 20:17:46 +0800</pubDate><guid>https://en.1991421.cn/2019/10/27/vscidea/</guid><description>&lt;blockquote>
&lt;p>The most used IDEs are IntelliJ IDEA and WebStorm. I recently switched from Navicat to DataGrip for database management, aiming to maintain a consistent operational habit, making applying one approach to everything easy. However, both IDEA and WebStorm are pretty heavy, and sometimes, launching IDEA or WebStorm can take a lot of work for simple tasks like editing a web page.&lt;/p>
&lt;p>VSC has become very popular over the years due to its lightweight nature and extensive plugin support. Therefore, I decided to modify VSC according to some operational habits from IDEA to improve efficiency.&lt;/p>
&lt;/blockquote>
&lt;h2 id="plugin-installation">
&lt;a class="heading-anchor-link" href="#plugin-installation">Plugin Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugin-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>IntelliJ IDEA Keybindings &lt;code>Keep most shortcuts consistent with IDEA&lt;/code>&lt;/li>
&lt;li>open in the browser &lt;code>Open files in browser&lt;/code>&lt;/li>
&lt;li>Darcula IntelliJ Theme &lt;code>Keep theme consistent with IDEA&lt;/code>&lt;/li>
&lt;li>Prettier - Code formatter&lt;/li>
&lt;li>EditorConfig for VS Code&lt;/li>
&lt;li>TSLint&lt;/li>
&lt;/ul>
&lt;p>And so on.&lt;/p>
&lt;h2 id="custom-settings">
&lt;a class="heading-anchor-link" href="#custom-settings">Custom Settings&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="custom-settings"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Change the shortcut for Open in Default Browser to &lt;code>⌥ F2&lt;/code> to match the preview in the browser shortcut in IDEA.&lt;/li>
&lt;/ul>
&lt;h2 id="final-notes">
&lt;a class="heading-anchor-link" href="#final-notes">Final Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Currently, not many plugins are installed, mainly because VSC&amp;rsquo;s role in my toolset is specific. Too many plugins might make VSC less &amp;ldquo;lightweight,&amp;rdquo; which needs to be controlled.&lt;/li>
&lt;li>After tuning and familiarization, I now use Macdown for MD editing and VSC for quick editing of simple HTML, JSON files, PlantUML, and lightweight projects. For larger projects, I still rely on launching IDEA or WebStorm. So, it&amp;rsquo;s the perfect toolset.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-27-121315.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="docs">
&lt;a class="heading-anchor-link" href="#docs">Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://dev.to/drsavvina/9--1-visual-studio-code-extensions-for-easier-and-faster-development-164n" target="_blank" rel="noopener">9 + 1 Visual Studio Code Extensions for Easier and Faster Development&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/lmk123/blog/issues/59" target="_blank" rel="noopener">So I switched from WebStorm to VS Code&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Dictionary vs Map vs Object</title><link>https://en.1991421.cn/2019/10/27/dictionary-vs-map-vs-object/</link><pubDate>Sun, 27 Oct 2019 15:16:11 +0800</pubDate><guid>https://en.1991421.cn/2019/10/27/dictionary-vs-map-vs-object/</guid><description>&lt;blockquote>
&lt;p>Frontend projects often use lodash, which includes a type called Dictionary. JavaScript has built-in objects Map and Object. What are the connections and differences between these three, and how should we choose between them? Read on!&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-27-061335.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="dictionary-in-lodash">
&lt;a class="heading-anchor-link" href="#dictionary-in-lodash">Dictionary in lodash&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dictionary-in-lodash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="concept-overview">
&lt;a class="heading-anchor-link" href="#concept-overview">Concept Overview&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concept-overview"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>A Dictionary is a data structure that stores data in key-value pairs. JavaScript&amp;rsquo;s Object, Map, and lodash&amp;rsquo;s Dictionary are all implementations of the dictionary type.&lt;/p>
&lt;/blockquote>
&lt;p>What is lodash? It&amp;rsquo;s a utility function library. Check the &lt;a href="https://lodash.com/" target="_blank" rel="noopener">official website&lt;/a> for details.&lt;/p>
&lt;p>lodash source code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">Dictionary&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">T&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>From this we can understand:&lt;/p>
&lt;ol>
&lt;li>lodash&amp;rsquo;s Dictionary is an interface type with string keys and generic values, meaning &lt;code>all values have consistent types&lt;/code> under this definition.&lt;/li>
&lt;li>lodash&amp;rsquo;s Dictionary is an instance of Object, so it also has Object methods.&lt;/li>
&lt;li>If a project doesn&amp;rsquo;t use lodash, you can implement a simple dictionary type yourself as shown above.&lt;/li>
&lt;/ol>
&lt;h2 id="map">
&lt;a class="heading-anchor-link" href="#map">Map&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="map"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;code>Map type was introduced in ES6 (ES2015)&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;p>Look at the TypeScript definition source code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">Map&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">K&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">V&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">clear&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>: &lt;span class="kt">K&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">callbackfn&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>: &lt;span class="kt">V&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">key&lt;/span>: &lt;span class="kt">K&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">map&lt;/span>: &lt;span class="kt">Map&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">K&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">V&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">thisArg?&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>: &lt;span class="kt">K&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">V&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">has&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>: &lt;span class="kt">K&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>: &lt;span class="kt">K&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>: &lt;span class="kt">V&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">size&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="map-object-initialization">
&lt;a class="heading-anchor-link" href="#map-object-initialization">Map Object Initialization&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="map-object-initialization"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">stuMap&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Map&lt;/span>&lt;span class="p">([[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;stu1&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;stu2&amp;#39;&lt;/span>&lt;span class="p">]])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>From this we know that Map not only stores structured data but also provides many property methods.&lt;/p>
&lt;h2 id="object">
&lt;a class="heading-anchor-link" href="#object">Object&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="object"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Object is the basic object in JavaScript, and basic objects are the foundation for defining and using other objects. Map is an instance of Object, and of course, lodash&amp;rsquo;s Dictionary is also an instance of Object.&lt;/p>
&lt;p>Look at the TypeScript definition source code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nb">Object&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nb">Function&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/** Returns a string representation of an object. */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">toString&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/** Returns a date converted to a string using the current locale. */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">toLocaleString&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/** Returns the primitive value of the specified object. */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">valueOf&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nb">Object&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Determines whether an object has a property with the specified name.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param v A property name.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hasOwnProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>: &lt;span class="kt">PropertyKey&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Determines whether an object exists in another object&amp;#39;s prototype chain.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param v Another object whose prototype chain is to be checked.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isPrototypeOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>: &lt;span class="kt">Object&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Determines whether a specified property is enumerable.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * @param v A property name.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">propertyIsEnumerable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>: &lt;span class="kt">PropertyKey&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: In TypeScript projects, when we declare an object type as {}, it&amp;rsquo;s equivalent to a hashMap type:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">o&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">{}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// should be equivalent to
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">o&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="p">{[&lt;/span>&lt;span class="nx">key&lt;/span>:&lt;span class="kt">string&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="kt">any&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">o&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">//fails
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">o&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;x&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">//succeeds
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="connections-and-differences-between-the-three">
&lt;a class="heading-anchor-link" href="#connections-and-differences-between-the-three">Connections and Differences Between the Three&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="connections-and-differences-between-the-three"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-27-072248.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol>
&lt;li>Object is similar to lodash&amp;rsquo;s Dictionary, but Dictionary only has consistent value types, while Object&amp;rsquo;s value types can be any type&lt;/li>
&lt;li>Map is ordered and iterable, while Object is not&lt;/li>
&lt;li>Map&amp;rsquo;s keys can be any type, while Object&amp;rsquo;s keys can only be number, string, or symbol.&lt;/li>
&lt;/ol>
&lt;h2 id="how-to-choose">
&lt;a class="heading-anchor-link" href="#how-to-choose">How to Choose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-choose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we understand the differences, let&amp;rsquo;s discuss common usage scenarios.&lt;/p>
&lt;ol>
&lt;li>Frontend-backend requests and responses are generally in &lt;code>application/json&lt;/code> format. If we need to define return value types, Object and Dictionary are better choices because &lt;code>JSON directly supports Object but not Map yet&lt;/code>. If the return data is always in the format {&amp;rsquo;&amp;rsquo;:&amp;rsquo;}, then you can directly define it as &lt;code>Dictionary&amp;lt;string&amp;gt;&lt;/code>. If the value types are not uniform, you can use Object or {}, but defining specific types is even better.&lt;/li>
&lt;li>If the data needs to consider order and iteration, then use Map.&lt;/li>
&lt;/ol>
&lt;h2 id="javascript-heap-and-stack">
&lt;a class="heading-anchor-link" href="#javascript-heap-and-stack">JavaScript Heap and Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="javascript-heap-and-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The types mentioned above all belong to the Object type, and object types are stored in heap memory, while stack memory only stores address references. So be careful when copying and modifying object values. Understanding this helps avoid pitfalls when using these types.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-27-070346.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we understand the relationships between these three, we can use them appropriately. One key point to highlight: when facing a scenario with simple data, Map, Object, and Dictionary can all solve the problem, but for performance, readability, etc., we still need to make reasonable choices to avoid creating pitfalls.&lt;/p>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map" target="_blank" rel="noopener">Map MDN&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/18541940/map-vs-object-in-javascript" target="_blank" rel="noopener">Map vs Object in JavaScript&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5c7f6251f265da2dce1f68d3#heading-11" target="_blank" rel="noopener">Differences and Usage Scenarios Between Object and Map&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://segmentfault.com/a/1190000009693516" target="_blank" rel="noopener">JavaScript Heap and Stack&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Type Issues When Using React with TypeScript</title><link>https://en.1991421.cn/2019/10/23/reacttypescript/</link><pubDate>Wed, 23 Oct 2019 23:18:22 +0800</pubDate><guid>https://en.1991421.cn/2019/10/23/reacttypescript/</guid><description>&lt;blockquote>
&lt;p>Frontend projects can improve code robustness with TypeScript support. Of course, during usage, you&amp;rsquo;ll inevitably encounter many type definition issues. Here&amp;rsquo;s a summary.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>To pursue quick solutions, we might lazily use &lt;code>// @ts-ignore&lt;/code>, but the downside of this approach is giving up type safety. So, avoid it if possible - it&amp;rsquo;s better to thoroughly solve the problem.&lt;/strong>&lt;/p>
&lt;h2 id="using-children-in-function-components">
&lt;a class="heading-anchor-link" href="#using-children-in-function-components">Using children in Function Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-children-in-function-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>For stateless components, we often use function components for declaration, and frequently use children for object transparency.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">React&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">IProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">permission&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">AuthShow&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">permission&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">children&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PropsWithChildren&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">IProps&lt;/span>&lt;span class="p">&amp;gt;)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&amp;gt;{&lt;/span>&lt;span class="nx">isPermit&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">children&lt;/span> : &lt;span class="kt">null&lt;/span>&lt;span class="p">}&amp;lt;/&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">AuthShow&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="redux-store-configuration-error">
&lt;a class="heading-anchor-link" href="#redux-store-configuration-error">Redux Store Configuration Error&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-store-configuration-error"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Error:(26, 21) TS2741: Property &amp;lsquo;[Symbol.observable]&amp;rsquo; is missing in type &amp;lsquo;Store&amp;lt;IRootState, AnyAction&amp;gt; &amp;amp; { dispatch: {}; }&amp;rsquo; but required in type &amp;lsquo;Store&amp;lt;any, AnyAction&amp;gt;&amp;rsquo;.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsx" data-lang="jsx">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Provider&lt;/span> &lt;span class="na">store&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Component&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Provider&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Solution: &lt;code>update redux from 4.0.0 to 4.0.3&lt;/code>&lt;/p>
&lt;p>&lt;a href="https://github.com/reduxjs/redux/issues/3466" target="_blank" rel="noopener">Related GitHub issue discussion&lt;/a>&lt;/p>
&lt;h2 id="type-error-when-using-withrouter">
&lt;a class="heading-anchor-link" href="#type-error-when-using-withrouter">Type Error When Using withRouter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-error-when-using-withrouter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Error:(18, 27) TS2345: Argument of type &amp;rsquo;typeof Hello&amp;rsquo; is not assignable to parameter of type &amp;lsquo;ComponentClass&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;, any&amp;gt; | FunctionComponent&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;raquo; | (FunctionComponent&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;raquo; &amp;amp; ComponentClass&amp;lt;&amp;hellip;&amp;gt;) | (ComponentClass&amp;lt;&amp;hellip;&amp;gt; &amp;amp; FunctionComponent&amp;lt;&amp;hellip;&amp;gt;)&amp;rsquo;.
Type &amp;rsquo;typeof Hello&amp;rsquo; is not assignable to type &amp;lsquo;ComponentClass&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;, any&amp;gt;&amp;rsquo;.
Types of parameters &amp;lsquo;props&amp;rsquo; and &amp;lsquo;props&amp;rsquo; are incompatible.
Property &amp;rsquo;name&amp;rsquo; is missing in type &amp;lsquo;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;&amp;rsquo; but required in type &amp;lsquo;Readonly&lt;IProps>&amp;rsquo;.&lt;/p>
&lt;/blockquote>
&lt;p>There are two easily confused route interface types: &lt;code>RouteComponentProps&lt;/code> and &lt;code>RouteProps&lt;/code>, but pay attention to their usage. When we use the withRouter higher-order component, the corresponding component&amp;rsquo;s props should inherit from &lt;code>RouteComponentProps&lt;/code>, not &lt;code>RouteProps&lt;/code>!&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">IProps&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">RouteComponentProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">withRouter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">component&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="when-to-use-routeprops">
&lt;a class="heading-anchor-link" href="#when-to-use-routeprops">When to Use RouteProps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-to-use-routeprops"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For route definitions, for example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">routesConfig&lt;/span>: &lt;span class="kt">RouteProps&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/hello&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">component&lt;/span>: &lt;span class="kt">HelloPage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="antd-form-and-redux-integration">
&lt;a class="heading-anchor-link" href="#antd-form-and-redux-integration">Antd Form and Redux Integration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="antd-form-and-redux-integration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>In individual components, you might encounter situations where antd form and redux need to be integrated. Below is the &lt;em>correct usage order, note that connect is on the outermost layer&lt;/em>.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">IProps&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">DispatchProps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">StateProps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">FormComponentProps&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">component&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Form&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">IProps&lt;/span>&lt;span class="p">&amp;gt;()(&lt;/span>&lt;span class="nx">Hello&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">connect&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapStateToProps&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapDispatchToProps&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)(&lt;/span>&lt;span class="nx">component&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="ref">
&lt;a class="heading-anchor-link" href="#ref">ref&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ref"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Ref is used for component communication. Looking at online articles, you&amp;rsquo;ll find that sometimes ref is used, sometimes wrappedComponentRef is used, and you&amp;rsquo;ll encounter some pitfalls.&lt;/p>
&lt;/blockquote>
&lt;h3 id="a-few-details">
&lt;a class="heading-anchor-link" href="#a-few-details">A Few Details&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="a-few-details"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>ref is an official React attribute, wrappedComponentRef is from antd&lt;/li>
&lt;li>Redux&amp;rsquo;s connect higher-order component doesn&amp;rsquo;t expose ref objects by default. If you need to use them, you need to configure &lt;code>forwardRef&lt;/code>&lt;/li>
&lt;li>Because we need to configure the fourth parameter of connect, the third one must be configured. As defined, it&amp;rsquo;s a function, so we need the following syntax, otherwise type errors will still occur&lt;/li>
&lt;/ul>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Below is a component wrapped by connect. If we need to use this component&amp;rsquo;s ref:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">connect&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapDispatchToProps&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">mapDispatchToProps&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">forwardRef&lt;/span>: &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)(&lt;/span>&lt;span class="nx">SList&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> &amp;lt;SList ref={this.solutionQuoteDiscountRef}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above is the correct configuration method. &lt;code>If forwardRef is not configured, this.solutionQuoteDiscountRef.current will actually get the connect component&lt;/code>, so you can&amp;rsquo;t directly call a specific method of the component.&lt;/p>
&lt;h3 id="when-to-use-wrappedcomponentref">
&lt;a class="heading-anchor-link" href="#when-to-use-wrappedcomponentref">When to Use wrappedComponentRef?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-to-use-wrappedcomponentref"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>As mentioned above, when antd form wraps a component, you can use it. This is also the officially recommended approach.&lt;/p>
&lt;h2 id="using-ref-to-access-form-values-in-antd-button-and-other-ui-components">
&lt;a class="heading-anchor-link" href="#using-ref-to-access-form-values-in-antd-button-and-other-ui-components">Using ref to Access Form Values in Antd Button and Other UI Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-ref-to-access-form-values-in-antd-button-and-other-ui-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes you need to use ref to directly get the current values of text boxes and input fields. Usage is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">textAreaRef&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createRef&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">TextArea&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">TextArea&lt;/span> &lt;span class="na">rows&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">ref&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">textAreaRef&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">cols&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Get value
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">textAreaRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">current&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">textAreaRef&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Getting the value as shown above works, but causes syntax errors in TS. The current solution is to ignore it, but this isn&amp;rsquo;t elegant.&lt;/p>
&lt;blockquote>
&lt;p>Error:(125, 91) TS2341: Property &amp;rsquo;textAreaRef&amp;rsquo; is private and only accessible within class &amp;lsquo;TextArea&amp;rsquo;.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error-when-using-debounce-in-lodash">
&lt;a class="heading-anchor-link" href="#error-when-using-debounce-in-lodash">Error When Using debounce in lodash&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-when-using-debounce-in-lodash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">doSearch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">debounce&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">doSearch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">700&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">doSearch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cancel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Using it as shown above will cause the following type error:&lt;/p>
&lt;blockquote>
&lt;p>Error:(216, 21) TS2339: Property &amp;lsquo;cancel&amp;rsquo; does not exist on type &amp;lsquo;() =&amp;gt; Promise&lt;void>&amp;rsquo;.&lt;/p>
&lt;/blockquote>
&lt;p>Solution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">debouncedDoSearch&lt;/span>: &lt;span class="kt">Function&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Cancelable&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">debouncedDoSearch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">debounce&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">doSearch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">700&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">debouncedDoSearch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cancel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">debouncedDoSearch&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="error-when-using-withrouter-and-injectintl-together">
&lt;a class="heading-anchor-link" href="#error-when-using-withrouter-and-injectintl-together">Error When Using withRouter and injectIntl Together&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-when-using-withrouter-and-injectintl-together"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Error:(188, 14) TS2345: Argument of type &amp;lsquo;ComponentClass&amp;lt;Pick&amp;lt;any, string | number | symbol&amp;gt;, any&amp;gt; &amp;amp; { WrappedComponent: ComponentType&lt;any>; }&amp;rsquo; is not assignable to parameter of type &amp;lsquo;ComponentClass&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;, any&amp;gt; | FunctionComponent&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;raquo; | (FunctionComponent&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;raquo; &amp;amp; ComponentClass&amp;lt;&amp;hellip;&amp;gt;) | (ComponentClass&amp;lt;&amp;hellip;&amp;gt; &amp;amp; FunctionComponent&amp;lt;&amp;hellip;&amp;gt;)&amp;rsquo;.
Type &amp;lsquo;ComponentClass&amp;lt;Pick&amp;lt;any, string | number | symbol&amp;gt;, any&amp;gt; &amp;amp; { WrappedComponent: ComponentType&lt;any>; }&amp;rsquo; is not assignable to type &amp;lsquo;ComponentClass&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;, any&amp;gt;&amp;rsquo;.
Type &amp;lsquo;Component&amp;lt;Pick&amp;lt;any, string | number | symbol&amp;gt;, any, any&amp;gt;&amp;rsquo; is not assignable to type &amp;lsquo;Component&amp;lt;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;, any, any&amp;gt;&amp;rsquo;.
Type &amp;lsquo;Pick&amp;lt;any, string | number | symbol&amp;gt;&amp;rsquo; is missing the following properties from type &amp;lsquo;RouteComponentProps&amp;lt;any, StaticContext, any&amp;gt;&amp;rsquo;: history, location, match&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">connect&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapStateToProps&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapDispatchToProps&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)(&lt;/span>&lt;span class="nx">withRouter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">injectIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">BDetailPage&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add type inference: injectIntl&lt;IProps>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">connect&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapStateToProps&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mapDispatchToProps&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)(&lt;/span>&lt;span class="nx">withRouter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">injectIntl&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">IProps&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nx">BDetailPage&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Both &lt;code>// @ts-ignore&lt;/code> and &lt;code>any&lt;/code> are like cheating - use them as little as possible. TypeScript&amp;rsquo;s purpose lies in types and assertions. If you directly cheat, the static analysis itself can&amp;rsquo;t function properly. This could then lead to serious bugs. So please respect types and understand the importance of explicit typing.&lt;/li>
&lt;li>TS syntax analysis errors differ slightly from TSLint errors. Lint errors directly tell you which rule was violated, and you just need to fix it accordingly. But TS syntax errors can sometimes be very long and intimidating, but if you read them carefully and parse them correctly, they&amp;rsquo;re still quite solvable. So don&amp;rsquo;t give up easily.&lt;/li>
&lt;/ol></description></item><item><title>Governing the Frontend API Layer</title><link>https://en.1991421.cn/2019/10/16/frontend-api-governance/</link><pubDate>Wed, 16 Oct 2019 23:03:25 +0800</pubDate><guid>https://en.1991421.cn/2019/10/16/frontend-api-governance/</guid><description>&lt;blockquote>
&lt;p>The React project I&amp;rsquo;m currently working on is gradually showing chaos in API usage, so I started the governance process. Here&amp;rsquo;s a record of the causes and consequences.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-16-150440.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="before-governance">
&lt;a class="heading-anchor-link" href="#before-governance">Before Governance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="before-governance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;em>Background: The number of API requests in the project has reached 100+.&lt;/em>&lt;/p>
&lt;p>Take an example of two API files, user-api.ts and setting-api.ts:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">getUserList&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>: &lt;span class="kt">IUserQuery&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/user/api/v1/users&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">params&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">getCountrySetting&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>:&lt;span class="kt">ICountrySettingParam&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/setting/api/v1/country&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">params&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Current APIs are all directly exported specific functions. This design has the following problems:&lt;/p>
&lt;ol>
&lt;li>Naming limitations: Because there&amp;rsquo;s no concept of namespaces, function names need to accurately express what the request does. Of course, they should also indicate that it&amp;rsquo;s an API request. As shown above, it&amp;rsquo;s hard to tell that it&amp;rsquo;s an API request when using it. Additionally, team members&amp;rsquo; skills vary, and without constraints, API naming becomes chaotic.&lt;/li>
&lt;li>Redundant base paths: In a single API file, many requests have the same prefix, causing repetition.&lt;/li>
&lt;li>Tight coupling: API files are directly imported wherever they&amp;rsquo;re used. If any changes occur, the outer layers need to be modified accordingly.&lt;/li>
&lt;/ol>
&lt;h2 id="after-governance">
&lt;a class="heading-anchor-link" href="#after-governance">After Governance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="after-governance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To improve the maintainability of the API layer, the following solution was finally adopted.&lt;/p>
&lt;p>First, let&amp;rsquo;s show the code&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">BaseApi&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">basePath&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">basePath&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">basePath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">basePath&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">UserApi&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">BaseApi&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">getAll&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>: &lt;span class="kt">IUserQuery&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">basePath&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">/v1/users`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">params&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">userApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">UserApi&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/user/api&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">userApi&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./user-api&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="benefits-after-improvement">
&lt;a class="heading-anchor-link" href="#benefits-after-improvement">Benefits After Improvement&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benefits-after-improvement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Using object-oriented approach makes each API module cohesive and easy to manage, e.g., shared basePath can be extracted.&lt;/li>
&lt;li>With class objects (namespaces), using &lt;code>userApi.getUserList&lt;/code> clearly indicates it&amp;rsquo;s an API request when reading code.&lt;/li>
&lt;li>The index file directly exports API instances for easy use, avoiding importing multiple API files, and internal API changes are somewhat opaque to external layers.&lt;/li>
&lt;li>As a class, theoretically we can instantiate different objects with different property values, which improves flexibility.&lt;/li>
&lt;/ul>
&lt;h3 id="api-method-naming-guidelines">
&lt;a class="heading-anchor-link" href="#api-method-naming-guidelines">API Method Naming Guidelines&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="api-method-naming-guidelines"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The above only addresses API management. To improve the readability of individual APIs, the team discussed and formed a set of naming guidelines&lt;/p>
&lt;p>&lt;code>Need continuous improvement&lt;/code>.&lt;/p>
&lt;p>&lt;em>Recommendations&lt;/em>&lt;/p>
&lt;ul>
&lt;li>getOne*&lt;/li>
&lt;li>getAll*&lt;/li>
&lt;li>update*&lt;/li>
&lt;li>create*&lt;/li>
&lt;li>delete*&lt;/li>
&lt;li>export*&lt;/li>
&lt;li>download*&lt;/li>
&lt;/ul>
&lt;ul>
&lt;li>is optional and depends on whether a noun is needed to more clearly reflect the method&amp;rsquo;s purpose.&lt;/li>
&lt;/ul>
&lt;h2 id="api-layer-design-in-angular">
&lt;a class="heading-anchor-link" href="#api-layer-design-in-angular">API Layer Design in Angular&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="api-layer-design-in-angular"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Because I did Angular development for 1-2 years before, I&amp;rsquo;ll extend this to discuss how it&amp;rsquo;s done in Angular.&lt;/p>
&lt;p>In Angular, we create ApiService classes where each request is a function. When used in specific components, we get an instance of this ApiService and then operate on a specific request. This scheme seems familiar?&lt;/p>
&lt;p>Yes, the governance scheme above is basically consistent with the official Angular scheme. So, behind technology are principles and design, which are framework-agnostic.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After quickly adjusting the API layer, the overall look is more comfortable now — happy. But the path of governance and refactoring never stops; let&amp;rsquo;s continue to improve.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://codeburst.io/how-to-call-api-in-a-smart-way-2ca572c6fe86" target="_blank" rel="noopener">How to make API calls in a smart way&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/@brybrophy/axios-core-api-object-oriented-javascript-love-effb37f14cd0" target="_blank" rel="noopener">Axios-Core-Api + Object-Oriented JavaScript = Love&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>interface vs type in TypeScript</title><link>https://en.1991421.cn/2019/10/15/typescriptinterface-vs-type/</link><pubDate>Tue, 15 Oct 2019 10:05:33 +0800</pubDate><guid>https://en.1991421.cn/2019/10/15/typescriptinterface-vs-type/</guid><description>&lt;blockquote>
&lt;p>In day-to-day TS code we reach for both &lt;code>interface&lt;/code> and &lt;code>type&lt;/code>. I tend to use interfaces for object shapes and type aliases for literal unions like &lt;code>type Username = 'bob' | 'kelly'&lt;/code>. I enforce it with the &lt;code>interface-over-type-literal&lt;/code> rule. But what’s the precise difference? Here’s a summary.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-the-docs-say">
&lt;a class="heading-anchor-link" href="#what-the-docs-say">What the Docs Say&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-the-docs-say"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Type aliases can act like interfaces; however, there are subtle differences. Interfaces create a new name; type aliases do not. Prefer interfaces when possible for better extension.&lt;/p>
&lt;p>Use type aliases when you need unions/tuples or shapes interfaces can’t express.&lt;/p>
&lt;/blockquote>
&lt;p>Docs: &lt;a href="http://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases" target="_blank" rel="noopener">http://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases&lt;/a>&lt;/p>
&lt;h3 id="key-points">
&lt;a class="heading-anchor-link" href="#key-points">Key Points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Type aliases don’t create a nominal type; interfaces do.&lt;/li>
&lt;li>Prefer interfaces for extensibility.&lt;/li>
&lt;li>Use type aliases for unions, tuples, or when you don’t want to expose lots of members.&lt;/li>
&lt;/ul>
&lt;h2 id="rule-interface-over-type-literal">
&lt;a class="heading-anchor-link" href="#rule-interface-over-type-literal">Rule: &lt;code>interface-over-type-literal&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rule-interface-over-type-literal"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This lint rule flags type literals defined via &lt;code>type&lt;/code>. Internally it checks:&lt;/p>
&lt;ul>
&lt;li>&lt;code>isTypeAliasDeclaration&lt;/code>&lt;/li>
&lt;li>&lt;code>isTypeLiteralNode&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Example from the rule’s tests:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">type&lt;/span> &lt;span class="n">T&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">number&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="err">❌&lt;/span> &lt;span class="n">flagged&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">type&lt;/span> &lt;span class="n">U&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">string&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="err">✅&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">type&lt;/span> &lt;span class="n">V&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">number&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">string&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">};&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="err">❌&lt;/span> &lt;span class="n">flagged&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">export&lt;/span> &lt;span class="n">type&lt;/span> &lt;span class="n">W&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">T&lt;/span> &lt;span class="p">};&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="err">❌&lt;/span> &lt;span class="n">flagged&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">type&lt;/span> &lt;span class="n">Record&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">U&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">K&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="p">]:&lt;/span> &lt;span class="n">U&lt;/span> &lt;span class="p">};&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="err">✅&lt;/span> &lt;span class="n">mapped&lt;/span> &lt;span class="n">types&lt;/span> &lt;span class="n">are&lt;/span> &lt;span class="n">allowed&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="takeaway">
&lt;a class="heading-anchor-link" href="#takeaway">Takeaway&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeaway"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use whichever fits the shape, but understand how tools treat them differently. Then enjoy the safety TS provides.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases" target="_blank" rel="noopener">Type Aliases&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5c2723635188252d1d34dc7d" target="_blank" rel="noopener">Interface vs. type (Chinese article)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/22687214" target="_blank" rel="noopener">TypeScript literal types (Chinese article)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.bountysource.com/issues/76022535-do-not-toggle-rule-interface-over-type-literal-for-mapped-types" target="_blank" rel="noopener">Do not toggle rule &amp;lsquo;interface-over-type-literal&amp;rsquo; for mapped types&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Essential JetBrains IDE Plugins</title><link>https://en.1991421.cn/2019/10/13/idea/</link><pubDate>Sun, 13 Oct 2019 22:17:53 +0800</pubDate><guid>https://en.1991421.cn/2019/10/13/idea/</guid><description>&lt;blockquote>
&lt;p>JetBrains products are indeed highly efficient. From the powerful IDE functionality itself, to rich shortcut key support, to the extensive plugin ecosystem, everything makes it worthy of being the strongest IDE currently available. Although JB lags behind in AI aspects now, I still rely on it in actual work.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>Continuously updated&lt;/code>&lt;/p>
&lt;p>Here are several plugins I use.&lt;/p>
&lt;h2 id="plugins">
&lt;a class="heading-anchor-link" href="#plugins">Plugins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="string-manipulationhttpspluginsjetbrainscomplugin2162-string-manipulation">
&lt;a class="heading-anchor-link" href="#string-manipulationhttpspluginsjetbrainscomplugin2162-string-manipulation">&lt;a href="https://plugins.jetbrains.com/plugin/2162-string-manipulation" target="_blank" rel="noopener">String Manipulation&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="string-manipulationhttpspluginsjetbrainscomplugin2162-string-manipulation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Frequently need to convert string variables to snake_case or other naming conventions? This plugin can quickly achieve that. It supplements the IDE&amp;rsquo;s built-in string operation capabilities.&lt;/p>
&lt;h3 id="gitlinkhttpsgithubcomben-gibsongitlink">
&lt;a class="heading-anchor-link" href="#gitlinkhttpsgithubcomben-gibsongitlink">&lt;a href="https://github.com/ben-gibson/GitLink" target="_blank" rel="noopener">GitLink&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gitlinkhttpsgithubcomben-gibsongitlink"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Quickly open the web address of project files in Git services. With one-click copy, you can directly obtain the online address of commit submissions including line numbers. It also supports custom Git sources, making it easy to configure support for unsupported Git services like Tencent Coding, Gitee, or internal Git services.&lt;/p>
&lt;/blockquote>
&lt;p>If you need to use it in editors like VSCode/Cursor, I recommend trying the &lt;a href="https://marketplace.visualstudio.com/items?itemName=AlanHe.cn-alanhe-gitlink" target="_blank" rel="noopener">Beautiful GitLink&lt;/a> plugin I developed. Personally, I think it&amp;rsquo;s even better than GitLink, but unfortunately, I&amp;rsquo;m not from a Java background and not skilled in Java development, so I haven&amp;rsquo;t contributed it to the JetBrains official plugin market.&lt;/p>
&lt;h3 id="gittoolboxhttpspluginsjetbrainscomplugin7499-gittoolbox">
&lt;a class="heading-anchor-link" href="#gittoolboxhttpspluginsjetbrainscomplugin7499-gittoolbox">&lt;a href="https://plugins.jetbrains.com/plugin/7499-gittoolbox" target="_blank" rel="noopener">GitToolBox&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gittoolboxhttpspluginsjetbrainscomplugin7499-gittoolbox"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Provides enhanced Git-related functionality, such as automatically fetching upstream code history. This is very important, as I often see colleagues forgetting to pull upstream code, leading to merge conflicts.&lt;/p>
&lt;/blockquote>
&lt;h3 id="github-copilothttpspluginsjetbrainscomplugin17718-github-copilot">
&lt;a class="heading-anchor-link" href="#github-copilothttpspluginsjetbrainscomplugin17718-github-copilot">&lt;a href="https://plugins.jetbrains.com/plugin/17718-github-copilot" target="_blank" rel="noopener">GitHub Copilot&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="github-copilothttpspluginsjetbrainscomplugin17718-github-copilot"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Provides AI code completion and chat functionality.&lt;/p>
&lt;/blockquote>
&lt;h3 id="ide-remote-controlhttpspluginsjetbrainscomplugin19991-ide-remote-control">
&lt;a class="heading-anchor-link" href="#ide-remote-controlhttpspluginsjetbrainscomplugin19991-ide-remote-control">&lt;a href="https://plugins.jetbrains.com/plugin/19991-ide-remote-control" target="_blank" rel="noopener">IDE Remote Control&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ide-remote-controlhttpspluginsjetbrainscomplugin19991-ide-remote-control"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Provides IDE remote control functionality, such as opening specific files in the IDE directly through HTTP requests.&lt;/p>
&lt;/blockquote>
&lt;h2 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Most JB plugins are compatible with WebStorm/IDEA and other products.&lt;/p>
&lt;h2 id="plugins-not-meeting-your-needs">
&lt;a class="heading-anchor-link" href="#plugins-not-meeting-your-needs">Plugins not meeting your needs?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugins-not-meeting-your-needs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Write your own, but you need to know Java development.&lt;/p>
&lt;p>The Plugin SDK is waiting for you to develop. &lt;a href="http://www.jetbrains.org/intellij/sdk/docs/welcome.html" target="_blank" rel="noopener">SDK&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Plugins are meant to compensate for the shortcomings of the IDE&amp;rsquo;s built-in functionality and fulfill personalized or specific requirements. Mastering them well can greatly enhance productivity&lt;/li>
&lt;li>While plugins are good, don&amp;rsquo;t overdo it. Control the quantity, otherwise: 1) usage becomes chaotic and inefficient, and 2) too many plugins increase the IDE&amp;rsquo;s performance overhead.&lt;/li>
&lt;/ol></description></item><item><title>React Debugging - Plugins</title><link>https://en.1991421.cn/2019/10/10/react/</link><pubDate>Thu, 10 Oct 2019 10:08:01 +0800</pubDate><guid>https://en.1991421.cn/2019/10/10/react/</guid><description>&lt;blockquote>
&lt;p>For React component debugging, besides the universal console, you can use react-devtools and Redux DevTools as auxiliary tools. Since teams always welcome new members, this is a brief summary for quick onboarding.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-10-013611.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Plugin installation requires accessing the Chrome Web Store. If you still can&amp;rsquo;t access Google&amp;hellip; well, I&amp;rsquo;d suggest you consider a career change. However, out of humanitarian concern, I&amp;rsquo;ll still explain how to install plugins when you can&amp;rsquo;t access Google.&lt;/em>&lt;/p>
&lt;h2 id="getting-plugins">
&lt;a class="heading-anchor-link" href="#getting-plugins">Getting Plugins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-plugins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://crxextractor.com/" target="_blank" rel="noopener">https://crxextractor.com/&lt;/a>
Enter the plugin store URL, click to download the crx file. Drag and drop it into Chrome to install.&lt;/p>
&lt;h2 id="react-devtools">
&lt;a class="heading-anchor-link" href="#react-devtools">react-devtools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-devtools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>react-devtools is an &lt;code>official&lt;/code> plugin by Facebook. Source code &lt;a href="https://github.com/facebook/react/tree/master/packages/react-devtools" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>&lt;code>In one sentence: The plugin is mainly used for React component structure analysis and component state monitoring.&lt;/code>&lt;/p>
&lt;h2 id="element-inspection">
&lt;a class="heading-anchor-link" href="#element-inspection">Element Inspection&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="element-inspection"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-10-react-devtools.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If the application is complex and we need to quickly locate the corresponding component code for a specific display content, then this feature is very useful.&lt;/p>
&lt;h3 id="inspection-not-working">
&lt;a class="heading-anchor-link" href="#inspection-not-working">Inspection Not Working?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="inspection-not-working"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Maybe inspection isn&amp;rsquo;t working due to the following reasons:&lt;/p>
&lt;ol>
&lt;li>webpack mode cannot be production - this mode compresses code, causing plugin inspection errors&lt;/li>
&lt;/ol>
&lt;h3 id="too-much-interference-from-context-and-host-elements">
&lt;a class="heading-anchor-link" href="#too-much-interference-from-context-and-host-elements">Too Much Interference from Context and Host Elements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="too-much-interference-from-context-and-host-elements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add filter&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-10-react-devtools-1.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="redux-devtools">
&lt;a class="heading-anchor-link" href="#redux-devtools">Redux DevTools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-devtools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This plugin is used for Redux state inspection and debugging analysis. Of course, if the app doesn&amp;rsquo;t use Redux, this plugin isn&amp;rsquo;t needed.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-10-013952.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol>
&lt;li>The tab toolbar on the right, such as diff, helps view state comparisons before and after each action execution&lt;/li>
&lt;li>The bottom toolbar supports importing and exporting data like JSON&lt;/li>
&lt;/ol>
&lt;p>&lt;code>In one sentence: The plugin serves to help us debug and analyze the data flow of the Redux state tree.&lt;/code>&lt;/p>
&lt;h2 id="recommended-resources">
&lt;a class="heading-anchor-link" href="#recommended-resources">Recommended Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=QFKZmBMgvus" target="_blank" rel="noopener">What&amp;rsquo;s New with React Dev Tools 4&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Redux-Thunk Source Code Walkthrough</title><link>https://en.1991421.cn/2019/10/05/redux-thunk/</link><pubDate>Sat, 05 Oct 2019 16:52:42 +0800</pubDate><guid>https://en.1991421.cn/2019/10/05/redux-thunk/</guid><description>&lt;blockquote>
&lt;p>redux-thunk is a middleware that lets Redux actions return functions. That makes async flows reusable—&lt;code>dispatch&lt;/code> can now trigger an async operation and decide when to fire the real action.&lt;/p>
&lt;p>Without thunk, every component that needs &lt;code>fetchUser()&lt;/code> must do &lt;code>fetchUser().then(...)&lt;/code> and dispatch manually. With thunk we wrap that logic once and dispatch the wrapper.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-05-025840.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="adding-thunk-to-a-project">
&lt;a class="heading-anchor-link" href="#adding-thunk-to-a-project">Adding Thunk to a Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-thunk-to-a-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">middleWares&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">sagaMiddleware&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">thunk&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">routerMiddleware&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">history&lt;/span>&lt;span class="p">)];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createStore&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rootReducer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">history&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">compose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">applyMiddleware&lt;/span>&lt;span class="p">(...&lt;/span>&lt;span class="nx">middleWares&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">reduxDevtools&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>applyMiddleware&lt;/code> stitches thunk (and any other middleware) into Redux. To understand thunk’s source, it helps to glance at Redux internals.&lt;/p>
&lt;h2 id="applymiddleware-in-redux">
&lt;a class="heading-anchor-link" href="#applymiddleware-in-redux">applyMiddleware in Redux&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="applymiddleware-in-redux"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The current Redux source is written in TypeScript. Relevant snippet:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">applyMiddleware&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">middlewares&lt;/span>: &lt;span class="kt">Middleware&lt;/span>&lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">StoreEnhancer&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">any&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">createStore&lt;/span>: &lt;span class="kt">StoreCreator&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">S&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">A&lt;/span> &lt;span class="na">extends&lt;/span> &lt;span class="na">AnyAction&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reducer&lt;/span>: &lt;span class="kt">Reducer&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">S&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">A&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">args&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">reducer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">dispatch&lt;/span>: &lt;span class="kt">Dispatch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Dispatching while constructing your middleware is not allowed. &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Other middleware would not be applied to this dispatch.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">middlewareAPI&lt;/span>: &lt;span class="kt">MiddlewareAPI&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getState&lt;/span>: &lt;span class="kt">store.getState&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dispatch&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">dispatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">middlewares&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">middleware&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">middleware&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">middlewareAPI&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dispatch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">compose&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">typeof&lt;/span> &lt;span class="na">dispatch&lt;/span>&lt;span class="p">&amp;gt;(...&lt;/span>&lt;span class="nx">chain&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dispatch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dispatch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>compose&lt;/code> is the standard functional helper that pipes functions from right to left:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">compose&lt;/span>&lt;span class="p">(...&lt;/span>&lt;span class="nx">funcs&lt;/span>: &lt;span class="kt">Function&lt;/span>&lt;span class="p">[])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">funcs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">T&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="nx">arg&lt;/span>: &lt;span class="kt">T&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">arg&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">funcs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">funcs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">funcs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reduce&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">(...&lt;/span>&lt;span class="nx">args&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">b&lt;/span>&lt;span class="p">(...&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The middleware chain wraps the original &lt;code>store.dispatch&lt;/code>. Later, when &lt;code>createStore&lt;/code> sees an enhancer, it invokes it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">createStore&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">S&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">A&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">Action&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Ext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">StateExt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">never&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reducer&lt;/span>: &lt;span class="kt">Reducer&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">S&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">A&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">preloadedState?&lt;/span>: &lt;span class="kt">PreloadedState&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">S&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">StoreEnhancer&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Ext&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">StateExt&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">enhancer?&lt;/span>: &lt;span class="kt">StoreEnhancer&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Ext&lt;/span>&lt;span class="err">,&lt;/span> &lt;span class="na">StateExt&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">enhancer&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s1">&amp;#39;undefined&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">enhancer&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s1">&amp;#39;function&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Expected the enhancer to be a function.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">enhancer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">createStore&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="nx">reducer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">preloadedState&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">PreloadedState&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">S&lt;/span>&lt;span class="p">&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... regular createStore logic
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So when thunk is part of &lt;code>middlewares&lt;/code>, it becomes one of the functions in &lt;code>chain&lt;/code> and can intercept dispatch calls.&lt;/p>
&lt;h2 id="redux-thunk-repository-layout">
&lt;a class="heading-anchor-link" href="#redux-thunk-repository-layout">redux-thunk Repository Layout&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-thunk-repository-layout"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Source: &lt;a href="https://github.com/reduxjs/redux-thunk" target="_blank" rel="noopener">https://github.com/reduxjs/redux-thunk&lt;/a>&lt;/p>
&lt;p>The project is tiny—&lt;code>src/index.js&lt;/code> holds the implementation, alongside TypeScript definition files, tests (using mocha/chai), and a standard toolchain (Babel, ESLint, Travis CI).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">├── src
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.d.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── index.js
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.js
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── typescript.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── ... other project config files
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="thunk-source-code">
&lt;a class="heading-anchor-link" href="#thunk-source-code">Thunk Source Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="thunk-source-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Only 12 lines:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">createThunkMiddleware&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">extraArgument&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">dispatch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">getState&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">typeof&lt;/span> &lt;span class="nx">action&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;function&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dispatch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">getState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">extraArgument&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">thunk&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createThunkMiddleware&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">thunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">withExtraArgument&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createThunkMiddleware&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="nx">thunk&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It’s a curried function. If the dispatched “action” is a function, thunk invokes it, passing &lt;code>dispatch&lt;/code>, &lt;code>getState&lt;/code>, and an optional extra argument. Otherwise it passes the action along to the next middleware (or the original dispatch).&lt;/p>
&lt;h2 id="thunk-vs-saga">
&lt;a class="heading-anchor-link" href="#thunk-vs-saga">Thunk vs. Saga&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="thunk-vs-saga"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>redux-saga is another middleware option. It watches for plain-object actions and coordinates side effects with generators. In contrast, thunk changes what &lt;code>dispatch&lt;/code> accepts—functions instead of plain objects.&lt;/p>
&lt;p>Both can handle async flows, but they suit different needs:&lt;/p>
&lt;ul>
&lt;li>Thunk keeps things simple for basic async calls. Each thunk decides when to dispatch.&lt;/li>
&lt;li>Saga separates concerns with watchers and effects—better for complex orchestration, retries, cancellation, or long-lived workflows.&lt;/li>
&lt;/ul>
&lt;p>Many teams (including mine) eventually migrate to saga when thunks grow into nested callbacks.&lt;/p>
&lt;h2 id="takeaways">
&lt;a class="heading-anchor-link" href="#takeaways">Takeaways&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeaways"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>redux-thunk rewrites &lt;code>dispatch&lt;/code> so it can accept functions. That’s the entire trick.&lt;/li>
&lt;li>For small apps with straightforward data flows, thunk is enough. When async logic spans multiple steps, sagas offer cleaner structure.&lt;/li>
&lt;li>Reading even tiny libraries like thunk sharpens fundamentals—and highlights how elegant a simple solution can be.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://sosout.github.io/2018/09/09/redux-thunk-source-analysis.html" target="_blank" rel="noopener">Comprehensive redux-thunk source analysis&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Currying" target="_blank" rel="noopener">Currying (Wikipedia)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5af13664f265da0ba266efcf" target="_blank" rel="noopener">JavaScript Currying Deep Dive&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Modern Frontend Optimization: A Deep Dive into Tree Shaking</title><link>https://en.1991421.cn/2019/10/04/tree-shaking/</link><pubDate>Fri, 04 Oct 2019 22:01:03 +0800</pubDate><guid>https://en.1991421.cn/2019/10/04/tree-shaking/</guid><description>&lt;blockquote>
&lt;p>While Single Page Applications (SPAs) provide a fluid frontend experience, they introduce challenges like SEO optimization and ballooning bundle sizes. Fortunately, modern build tools offer solutions like Tree Shaking to prune unused code and optimize performance.&lt;/p>
&lt;/blockquote>
&lt;img src="https://static.1991421.cn/2019-10-02-150553.jpg" style="zoom:33%;" alt="Tree Shaking Concept" />
&lt;h2 id="what-is-tree-shaking">
&lt;a class="heading-anchor-link" href="#what-is-tree-shaking">What is Tree Shaking?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-tree-shaking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Tree shaking is a form of &lt;strong>Dead Code Elimination (DCE)&lt;/strong>. The term was popularized by the Rollup community and is now a core feature of Webpack. It refers to the process where the bundler &amp;ldquo;shakes&amp;rdquo; the dependency tree of your application to remove any modules or functions that are imported but never actually called.&lt;/p>
&lt;h3 id="core-principles">
&lt;a class="heading-anchor-link" href="#core-principles">Core Principles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="core-principles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Static Analysis&lt;/strong>: Tree shaking relies on the static structure of ES2015 module syntax (&lt;code>import&lt;/code> and &lt;code>export&lt;/code>).&lt;/li>
&lt;li>&lt;strong>ESM over CommonJS&lt;/strong>: Unlike &lt;code>require&lt;/code> and &lt;code>module.exports&lt;/code> (CommonJS), which are dynamic, ESM is static. This allows the compiler to determine what is used &lt;em>before&lt;/em> the code actually runs.&lt;/li>
&lt;li>&lt;strong>Pure Functions &amp;amp; Side Effects&lt;/strong>: Modern bundlers use the &lt;code>sideEffects&lt;/code> property in &lt;code>package.json&lt;/code> to identify which parts of a project are &amp;ldquo;pure&amp;rdquo; and safe to remove if unused.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="essential-tool-webpack-bundle-analyzer">
&lt;a class="heading-anchor-link" href="#essential-tool-webpack-bundle-analyzer">Essential Tool: Webpack Bundle Analyzer&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="essential-tool-webpack-bundle-analyzer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To optimize, you first need to measure. The &lt;code>webpack-bundle-analyzer&lt;/code> plugin provides a visual treemap of your bundle, making it obvious which dependencies are taking up the most space.&lt;/p>
&lt;h3 id="installation--setup">
&lt;a class="heading-anchor-link" href="#installation--setup">Installation &amp;amp; Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation--setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Install the plugin&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev webpack-bundle-analyzer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Add a script to &lt;code>package.json&lt;/code>&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;analyze&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;node scripts/build.js --analyzer&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Configure Webpack&lt;/strong> (&lt;code>webpack.config.prod.js&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">BundleAnalyzerPlugin&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;webpack-bundle-analyzer&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">BundleAnalyzerPlugin&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">argv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;--analyzer&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">BundleAnalyzerPlugin&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Running &lt;code>npm run analyze&lt;/code> will launch a local server (default port 8888) showing a detailed breakdown of your production bundle.&lt;/p>
&lt;hr>
&lt;h2 id="practical-example-project-code--third-party-libraries">
&lt;a class="heading-anchor-link" href="#practical-example-project-code--third-party-libraries">Practical Example: Project Code &amp;amp; Third-Party Libraries&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practical-example-project-code--third-party-libraries"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s look at how tree shaking works in a real-world project built with &lt;code>create-react-app&lt;/code>.&lt;/p>
&lt;h3 id="1-project-source-code">
&lt;a class="heading-anchor-link" href="#1-project-source-code">1. Project Source Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-project-source-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Imagine you have a utility file &lt;code>utils.js&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// utils.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">appendSuffix&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">str&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">str&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;_suffix&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">appendPrefix&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">str&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;prefix_&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">str&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If your application only imports &lt;code>appendPrefix&lt;/code>, a standard production build will identify that &lt;code>appendSuffix&lt;/code> is never called. Thanks to Webpack&amp;rsquo;s &lt;code>mode: 'production'&lt;/code>, which enables the &lt;code>TerserPlugin&lt;/code> by default, &lt;code>appendSuffix&lt;/code> will be completely removed from the final bundle.&lt;/p>
&lt;h3 id="2-trimming-third-party-libraries-lodash">
&lt;a class="heading-anchor-link" href="#2-trimming-third-party-libraries-lodash">2. Trimming Third-Party Libraries (Lodash)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-trimming-third-party-libraries-lodash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The standard &lt;code>lodash&lt;/code> package uses CommonJS, which is famously difficult to tree-shake. If you import one function, you often end up with the entire library.&lt;/p>
&lt;p>&lt;strong>The Solution: &lt;code>lodash-es&lt;/code>&lt;/strong>
By switching to the ESM version of Lodash, you enable the bundler to pick only what you use.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Replace lodash with lodash-es&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm uninstall lodash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install lodash-es
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Standard (Bad for tree shaking)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="nx">_&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;lodash&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Optimized (Good for tree shaking)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">map&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;lodash-es&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-optimizing-ui-frameworks-ant-design">
&lt;a class="heading-anchor-link" href="#3-optimizing-ui-frameworks-ant-design">3. Optimizing UI Frameworks (Ant Design)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-optimizing-ui-frameworks-ant-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>UI libraries like Ant Design are massive. To avoid importing the entire library, use &lt;code>babel-plugin-import&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Install the plugin&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install babel-plugin-import --save-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Configure &lt;code>.babelrc&lt;/code>&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;plugins&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;import&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;libraryName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;antd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;libraryDirectory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;es&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;style&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;css&amp;#34;&lt;/span> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>This configuration ensures that when you write &lt;code>import { Button } from 'antd';&lt;/code>, only the Button component and its associated CSS are bundled.&lt;/p>
&lt;hr>
&lt;h2 id="understanding-side-effects">
&lt;a class="heading-anchor-link" href="#understanding-side-effects">Understanding Side Effects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="understanding-side-effects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A &amp;ldquo;side effect&amp;rdquo; occurs when a module does something other than just exporting values—for example, modifying a global variable or adding a polyfill to the &lt;code>window&lt;/code> object.&lt;/p>
&lt;p>If Webpack encounters a module with potential side effects, it won&amp;rsquo;t remove it, even if you don&amp;rsquo;t use any of its exports. You can hint to Webpack that your code is &amp;ldquo;pure&amp;rdquo; by adding this to your &lt;code>package.json&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;my-project&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sideEffects&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If some of your files &lt;em>do&lt;/em> have side effects (like CSS imports), list them explicitly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;sideEffects&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;*.css&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;./src/polyfills.js&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Tree shaking is a powerful tool, but it&amp;rsquo;s not magic. It requires you to write modern, static ES6 code and carefully choose your third-party dependencies. By auditing your bundle regularly and using ESM-ready libraries, you can keep your frontend applications fast and lightweight.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://webpack.js.org/guides/tree-shaking/" target="_blank" rel="noopener">Webpack Official: Tree Shaking Guide&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.google.com/web/fundamentals/performance/optimizing-javascript/tree-shaking/" target="_blank" rel="noopener">Google Developers: Reduce JS Payloads with Tree Shaking&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.blazemeter.com/blog/the-correct-way-to-import-lodash-libraries-a-benchmark/" target="_blank" rel="noopener">Lodash Benchmark: The Correct Way to Import&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>React Project Unit Testing</title><link>https://en.1991421.cn/2019/10/02/react/</link><pubDate>Wed, 02 Oct 2019 22:45:06 +0800</pubDate><guid>https://en.1991421.cn/2019/10/02/react/</guid><description>&lt;blockquote>
&lt;p>The previous article introduced Jest setup and how to run tests. This one focuses on testing React projects.&lt;/p>
&lt;/blockquote>
&lt;h2 id="project-tech-stack">
&lt;a class="heading-anchor-link" href="#project-tech-stack">Project tech stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-tech-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This is a general UI component library, so it does not involve Redux or APIs. The stack is relatively simple:&lt;/p>
&lt;ul>
&lt;li>React&lt;/li>
&lt;li>TypeScript&lt;/li>
&lt;li>Antd&lt;/li>
&lt;li>Less&lt;/li>
&lt;li>react-intl&lt;/li>
&lt;/ul>
&lt;h2 id="testing-objectives">
&lt;a class="heading-anchor-link" href="#testing-objectives">Testing objectives&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing-objectives"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When testing, decide what to test:&lt;/p>
&lt;ul>
&lt;li>Functional correctness of utility functions&lt;/li>
&lt;li>Component functionality (UI, interactivity, etc.)&lt;/li>
&lt;/ul>
&lt;h2 id="3a-principle">
&lt;a class="heading-anchor-link" href="#3a-principle">3A principle&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3a-principle"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Arrange&lt;/p>
&lt;p>The preparation part initializes objects and the input data for the method under test.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Act&lt;/p>
&lt;p>The execution part calls the method under test with prepared parameters.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Assert&lt;/p>
&lt;p>The assertion part verifies that execution matches expectations.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://docs.microsoft.com/zh-cn/visualstudio/test/unit-test-basics?view=vs-2015&amp;amp;redirectedfrom=MSDN" target="_blank" rel="noopener">See here&lt;/a>&lt;/p>
&lt;h2 id="test-setup">
&lt;a class="heading-anchor-link" href="#test-setup">Test setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="test-setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="creating-intl-enzyme-test-helper">
&lt;a class="heading-anchor-link" href="#creating-intl-enzyme-test-helper">Creating intl-enzyme-test-helper&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="creating-intl-enzyme-test-helper"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Because I don&amp;rsquo;t want to test internationalization, I mock it. Otherwise it throws intlProvider errors.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">React&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">IntlProvider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">intlShape&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-intl&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">mount&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">shallow&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;enzyme&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">messages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span> &lt;span class="c1">// en.json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">intlProvider&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">IntlProvider&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">locale&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;en&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">messages&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onError&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="p">{});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">intlProvider&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getChildContext&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">nodeWithIntlProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">React&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cloneElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">shallowWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">shallow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodeWithIntlProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">mount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodeWithIntlProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">childContextTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span>: &lt;span class="kt">intlShape&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">renderWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodeWithIntlProp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">childContextTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">intl&lt;/span>: &lt;span class="kt">intlShape&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">rendererWithIntl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">renderer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">IntlProvider&lt;/span> &lt;span class="na">locale&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;en&amp;#39;&lt;/span> &lt;span class="na">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">messages&lt;/span>&lt;span class="p">}&amp;gt;{&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">}&amp;lt;/&lt;/span>&lt;span class="nt">IntlProvider&lt;/span>&lt;span class="p">&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Complete code: &lt;a href="https://gist.github.com/alanhe421/93445ee840866586dac30a67cf4f6822" target="_blank" rel="noopener">see here&lt;/a>&lt;/p>
&lt;h4 id="suppress-intl-missing-errors">Suppress intl missing errors&lt;/h4>&lt;p>Because &lt;code>const messages = {}; // en.json&lt;/code> has no translations, tests pass but log errors. We suppress them with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onError&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-02-132743.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="creating-enzyme-setup">
&lt;a class="heading-anchor-link" href="#creating-enzyme-setup">Creating enzyme-setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="creating-enzyme-setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Component tests use enzyme, so enable the corresponding adapter:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">configure&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;enzyme&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">Adapter&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;enzyme-adapter-react-16&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// React 16 Enzyme adapter
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">configure&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">adapter&lt;/span>: &lt;span class="kt">new&lt;/span> &lt;span class="nx">Adapter&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Jest also needs config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setupFiles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/test/enzyme-setup.ts&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If not configured, it throws:&lt;/p>
&lt;blockquote>
&lt;p>Error:
Enzyme Internal Error: Enzyme expects an adapter to be configured, but found none.
To configure an adapter, you should call &lt;code>Enzyme.configure({ adapter: new Adapter() })&lt;/code>
before using any of Enzyme&amp;rsquo;s top level APIs, where &lt;code>Adapter&lt;/code> is the adapter
corresponding to the library currently being tested. For example:&lt;/p>
&lt;/blockquote>
&lt;h2 id="utility-function-testing">
&lt;a class="heading-anchor-link" href="#utility-function-testing">Utility function testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="utility-function-testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This is the simplest: decide input and output. For frontend projects using Redux, testing a reducer is similar. I see them as pure functions (input -&amp;gt; output).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">formatPrice&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../components/utils/common-utils&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;common-utils test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should return formatted price when value is 0 or undefined or null&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">formatPrice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">formatPrice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">undefined&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">formatPrice&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="component-testing">
&lt;a class="heading-anchor-link" href="#component-testing">Component testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Component testing uses enzyme&amp;rsquo;s mount, render, shallow.&lt;/p>
&lt;h3 id="differences-between-render-mount-and-shallow">
&lt;a class="heading-anchor-link" href="#differences-between-render-mount-and-shallow">Differences between render, mount, and shallow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="differences-between-render-mount-and-shallow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>render uses Cheerio, and returns static HTML. It is good for snapshot tests. shallow and mount return React trees (not DOM). If you use React DevTools, these appear under the React tab. render gives the Elements tab output.&lt;/p>
&lt;p>The bigger difference is that shallow and mount return a ReactWrapper with methods like find(), parents(), children(), state(), props(), setState(), setProps(), simulate().&lt;/p>
&lt;p>shallow renders only the current component. mount renders current and child components. For interaction tests, I usually use mount. But mount is heavier and uses more memory. If you do not need to assert child components, use shallow.&lt;/p>
&lt;/blockquote>
&lt;h3 id="component-rendering-and-interaction-tests">
&lt;a class="heading-anchor-link" href="#component-rendering-and-interaction-tests">Component rendering and interaction tests&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-rendering-and-interaction-tests"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>LCollapsedDescription is a description component with collapse functionality.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="nx">React&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">LCollapsedDescription&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">LDescriptionItem&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../components/collapsed-description&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../intl-enzyme-test-helper&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;collapsed-description component test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">items&lt;/span>: &lt;span class="kt">LDescriptionItem&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;1111&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;date&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;2019/10/01&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;price&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>: &lt;span class="kt">1000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should render all description items data&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">wrapper&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">LCollapsedDescription&lt;/span> &lt;span class="na">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">items&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;td&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should render two description items data when limit is 2&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">wrapper&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">LCollapsedDescription&lt;/span> &lt;span class="na">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">items&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">limit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;td&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Icon&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">simulate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;click&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;td&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Icon&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">simulate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;click&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;td&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should render description items each row data&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">wrapper1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">LCollapsedDescription&lt;/span> &lt;span class="na">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">items&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tr&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">at&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">children&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">wrapper2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mountWithIntl&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">LCollapsedDescription&lt;/span> &lt;span class="na">data&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">items&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">column&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="p">/&amp;gt;);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tr&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">at&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">children&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tr&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">at&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">children&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toBe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ol>
&lt;li>In case1, if you change &lt;code>mountWithIntl&lt;/code> to &lt;code>shallowWithIntl&lt;/code>, the test fails because shallow only renders the current component. &lt;code>renderWithIntl&lt;/code> will work.&lt;/li>
&lt;li>In case2, if you change &lt;code>mountWithIntl&lt;/code> to &lt;code>renderWithIntl&lt;/code>, the test fails because render produces DOM nodes and cannot find React components (capitalized tags). Also, simulate does not exist.&lt;/li>
&lt;/ol>
&lt;p>These examples clarify differences. Use each as needed.&lt;/p>
&lt;h2 id="snapshot-testing">
&lt;a class="heading-anchor-link" href="#snapshot-testing">Snapshot testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="snapshot-testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Snapshot testing saves a snapshot of the rendered output on the first run, and compares later runs against it.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">import&lt;/span> &lt;span class="nx">renderer&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;react-test-renderer&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should enable children elements when enable is true&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">wrapper&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">renderer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&amp;lt;&lt;/span>&lt;span class="nt">AuthEnable&lt;/span> &lt;span class="na">enable&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="nx">click&lt;/span> &lt;span class="nx">it&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">button&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">AuthEnable&lt;/span>&lt;span class="p">&amp;gt;).&lt;/span>&lt;span class="nx">toJSON&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">wrapper&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toMatchSnapshot&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>Snapshot tests prevent unintentional structural/style changes.&lt;/li>
&lt;li>They are only a supplement.&lt;/li>
&lt;/ol>
&lt;p>If you unintentionally change a component, UT failures show error info for troubleshooting.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-29-103412.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="enzyme-vs-react-test-renderer-snapshots">
&lt;a class="heading-anchor-link" href="#enzyme-vs-react-test-renderer-snapshots">Enzyme vs react-test-renderer snapshots&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enzyme-vs-react-test-renderer-snapshots"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>react-test-renderer snapshots are simpler and more readable, showing complete HTML.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-12-29-102932.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="supplementary">
&lt;a class="heading-anchor-link" href="#supplementary">Supplementary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="supplementary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Regarding testing in this UI library, the above covers basics. Beyond that is continuous enrichment.&lt;/p>
&lt;p>If we test Redux or redux-saga:&lt;/p>
&lt;h3 id="reducer-testing">
&lt;a class="heading-anchor-link" href="#reducer-testing">Reducer testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reducer-testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Redux has three parts: action, reducer, store. Actions are objects; not much to test. Reducers are pure functions, same as utility testing. Store is a big object; I do not think it needs tests. For component/effect tests, mock the store to desired values.&lt;/p>
&lt;h3 id="effects-testing">
&lt;a class="heading-anchor-link" href="#effects-testing">Effects testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="effects-testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In my projects, I extract request handling into the effects layer. The React component layer just renders data.&lt;/p>
&lt;blockquote>
&lt;p>The React component layer no longer directly handles API requests - this is the approach in my current project.&lt;/p>
&lt;/blockquote>
&lt;p>For effects tests:&lt;/p>
&lt;p>&lt;code>Note: API requests are not the focus - we only care whether data is correct, so mock them.&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">SagaSpecFactory&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">static&lt;/span> &lt;span class="nx">getAPIStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">apiMethodFunc&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>: &lt;span class="kt">object&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">any&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">fn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">fn&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">apiMethodFunc&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Complete code: &lt;a href="https://gist.github.com/alanhe421/cfe4d2e3ed6200669dc4e15455627c51" target="_blank" rel="noopener">see here&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;should init app when init app action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">initAppActionEffects&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">params&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">provide&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">call&lt;/span>: &lt;span class="kt">SagaSpecFactory.getAPIStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUserInfo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">data&lt;/span>: &lt;span class="kt">user&lt;/span> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">call&lt;/span>: &lt;span class="kt">SagaSpecFactory.getAPIStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getMenusByUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">menus&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUserInfo&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getMenusByUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">like&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">action&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">GlobalActionTypes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">INIT_MENU&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">menus&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">like&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">action&lt;/span>: &lt;span class="kt">initAppCompleteAction&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Regarding testing, I have two thoughts:&lt;/p>
&lt;ol>
&lt;li>Don&amp;rsquo;t test just for the sake of testing. If tests don&amp;rsquo;t bring benefit, don&amp;rsquo;t write them. Both dev and testing need ROI.&lt;/li>
&lt;li>Testing reduces manual test costs. Repeated testing is manual labor; automated tests improve efficiency and quality. Good tests can serve as living documentation. For teams, tests should be written, but the amount depends on the project.&lt;/li>
&lt;li>These are my superficial views - not all may be correct, but they are moving in the right direction.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/formatjs/react-intl/blob/master/docs/Testing-with-React-Intl.md#helper-function-1" target="_blank" rel="noopener">Testing with React-Intl&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://gist.github.com/mirague/c05f4da0d781a9b339b501f1d5d33c37/" target="_blank" rel="noopener">CustomComponent-test.js&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://echizen.github.io/tech/2017/02-12-jest-enzyme-method" target="_blank" rel="noopener">Testing React projects with jest+enzyme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/simply/snapshots-painless-testing-of-react-components-6bce3c4d51fc" target="_blank" rel="noopener">Snapshots: Painless Testing of React Components&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Hsueh-Jen/blog/issues/1" target="_blank" rel="noopener">Write React unit tests with jest+enzyme&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.microsoft.com/zh-cn/visualstudio/test/unit-test-basics?view=vs-2015&amp;amp;redirectedfrom=MSDN" target="_blank" rel="noopener">Microsoft unit testing basics&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Jest Unit Test Configuration</title><link>https://en.1991421.cn/2019/10/02/jest/</link><pubDate>Wed, 02 Oct 2019 21:09:05 +0800</pubDate><guid>https://en.1991421.cn/2019/10/02/jest/</guid><description>&lt;blockquote>
&lt;p>Recently working on a company-level UI component library, for testing, decided to continue using Jest. Pursuing simplicity, decisively abandoned the copy-paste shortcut and configured from scratch. Documenting this here.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-02-131039.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="understanding-concepts">
&lt;a class="heading-anchor-link" href="#understanding-concepts">Understanding Concepts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="understanding-concepts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Before playing, first understand what Jest does&lt;/p>
&lt;blockquote>
&lt;p>Jest is a delightful JavaScript Testing Framework with a focus on simplicity.
It works with projects using: Babel, TypeScript, Node, React, Angular, Vue and more!&lt;/p>
&lt;/blockquote>
&lt;p>The above is the official introduction, from which we know Jest is not just for React and JS.&lt;/p>
&lt;h2 id="frontend-technology-stack">
&lt;a class="heading-anchor-link" href="#frontend-technology-stack">Frontend Technology Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-technology-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Listing the technology stack used in my frontend project:&lt;/p>
&lt;ul>
&lt;li>React&lt;/li>
&lt;li>TypeScript&lt;/li>
&lt;li>antd&lt;/li>
&lt;/ul>
&lt;p>So, based on this, let&amp;rsquo;s configure testing&lt;/p>
&lt;h2 id="basic-configuration">
&lt;a class="heading-anchor-link" href="#basic-configuration">Basic Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="basic-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="packagejson">
&lt;a class="heading-anchor-link" href="#packagejson">package.json&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="packagejson"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Here only listing packages needed for UT installation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;devDependencies&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/enzyme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.1.13&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;23.3.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enzyme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enzyme-adapter-react-16&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.3.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;identity-obj-proxy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;23.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ts-jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;23.1.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="function-of-each-package">
&lt;a class="heading-anchor-link" href="#function-of-each-package">Function of Each Package&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="function-of-each-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>@types/**&lt;/p>
&lt;p>TS definition files for required libraries&lt;/p>
&lt;/li>
&lt;li>
&lt;p>enzyme&lt;/p>
&lt;p>A testing tool, note it can only be used for react&lt;/p>
&lt;/li>
&lt;li>
&lt;p>enzyme-adapter-react-16&lt;/p>
&lt;p>Serves react component testing&lt;/p>
&lt;/li>
&lt;li>
&lt;p>identity-obj-proxy&lt;/p>
&lt;p>Mock object, for example UT testing components might import images or less styles.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>jest&lt;/p>
&lt;p>Target testing framework, definitely need to install&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ts-jest&lt;/p>
&lt;p>Because we&amp;rsquo;re writing frontend projects with TS, so need to install this package. If we were writing frontend with JS, we could delete this package.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="jestconfjs">
&lt;a class="heading-anchor-link" href="#jestconfjs">jest.conf.js&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jestconfjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">transform&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;^.+\\.tsx?$&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;ts-jest&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">testMatch&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/test/**/+(*.)+(spec.ts?(x))&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">coverageDirectory&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/test-results/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">testPathIgnorePatterns&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/node_modules/&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">moduleFileExtensions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;ts&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;tsx&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;js&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;jsx&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;json&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;node&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">globals&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ts-jest&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tsConfigFile&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;./tsconfig.test.json&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">moduleNameMapper&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;\\.(less)$&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;identity-obj-proxy&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setupFiles&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;rootDir&amp;gt;/test/enzyme-setup.ts&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="script">
&lt;a class="heading-anchor-link" href="#script">script&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="script"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;jest --coverage --logHeapUsage --maxWorkers=2 --config ./jest.conf.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>coverage is to display test code coverage information&lt;/li>
&lt;li>maxWorkers is to speed up UT execution time, this value is recommended not to be too large.&lt;/li>
&lt;/ul>
&lt;p>More configuration, &lt;a href="https://jestjs.io/docs/en/cli" target="_blank" rel="noopener">see official website&lt;/a>&lt;/p>
&lt;p>About maxWorkers, you can see &lt;a href="https://www.peterbe.com/plog/ideal-number-of-workers-in-jest-maxWorkers" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;h3 id="key-configuration-points-in-jest">
&lt;a class="heading-anchor-link" href="#key-configuration-points-in-jest">Key Configuration Points in Jest&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-configuration-points-in-jest"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Because frontend components here import less files, module proxy is set up here.&lt;/li>
&lt;li>setupFiles is configured because I used enzyme for react component testing&lt;/li>
&lt;/ol>
&lt;h2 id="issues-encountered">
&lt;a class="heading-anchor-link" href="#issues-encountered">Issues Encountered&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="issues-encountered"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="test-match-0">
&lt;a class="heading-anchor-link" href="#test-match-0">test match 0&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="test-match-0"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When actually running UT, initially found match was 0. The direct reason is Jest couldn&amp;rsquo;t find our test cases.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-02-124010.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Solution is two points:&lt;/p>
&lt;ol>
&lt;li>Ensure testMatch path is correct&lt;/li>
&lt;li>Add moduleFileExtensions&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After the above configuration, executing the test command will show the output results below. Of course, Jest configuration is much more extensive than shown here - this is just a simple starting point. Next is to gradually improve and enrich based on actual circumstances.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-10-02-125402.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Although this is just a rough and brief introduction, I hope it can help everyone.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://jestjs.io/docs/en/configuration" target="_blank" rel="noopener">jest configuration - offcial doc &lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/yinxin630/blog/issues/22" target="_blank" rel="noopener">Jest Unit Testing Configuration and Solutions to Common Issues&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>JSX.Element vs React.ReactNode Comparison</title><link>https://en.1991421.cn/2019/09/28/jsx-element-vs-react-reactnode/</link><pubDate>Sat, 28 Sep 2019 12:44:18 +0800</pubDate><guid>https://en.1991421.cn/2019/09/28/jsx-element-vs-react-reactnode/</guid><description>&lt;blockquote>
&lt;p>Recently while looking at antd source code, I noticed these two type definitions for render elements: JSX.Element and React.ReactNode. I was curious about their differences, so I&amp;rsquo;m documenting it here.&lt;/p>
&lt;/blockquote>
&lt;p>First, the big conclusion [commonly known as stating the obvious]: they are not the same.&lt;/p>
&lt;h2 id="are-jsxelement-and-reactreactnode-equivalent">
&lt;a class="heading-anchor-link" href="#are-jsxelement-and-reactreactnode-equivalent">Are JSX.Element and React.ReactNode equivalent?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="are-jsxelement-and-reactreactnode-equivalent"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-28-040933.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Create a Card component with the content parameter defined as JSX.Element. If you pass an array element as a parameter, it directly reports an error. Changing it to &lt;code>JSX.Element[]&lt;/code> or &lt;code>React.ReactNode&lt;/code> both work fine.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-28-041055.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Does this mean they are equivalent?&lt;/p>
&lt;h2 id="are-jsxelement-and-reactreactnode-equivalent-1">
&lt;a class="heading-anchor-link" href="#are-jsxelement-and-reactreactnode-equivalent-1">Are JSX.Element[] and React.ReactNode equivalent?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="are-jsxelement-and-reactreactnode-equivalent-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>No, the ReactNode type definition is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">type&lt;/span> &lt;span class="nx">ReactNode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ReactChild&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">ReactFragment&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">ReactPortal&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kt">number&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">null&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">undefined&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This means it can accept various types, while Element must be a React Element [of course, it can be a native Element].&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">ReactElement&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">P&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">ComponentClass&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">P&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nx">SFC&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">P&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">props&lt;/span>: &lt;span class="kt">P&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">key&lt;/span>: &lt;span class="kt">Key&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For example, in the above case, if the type is defined as JSX.Element and content is a number, it will report a type error.&lt;/p>
&lt;h2 id="technical-explanation">
&lt;a class="heading-anchor-link" href="#technical-explanation">Technical Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="technical-explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Quoted from an expert&amp;rsquo;s answer:&lt;/p>
&lt;blockquote>
&lt;p>Quote @ferdaber: A more technical explanation is that a valid React node is not the same thing as what is returned by React.createElement. Regardless of what a component ends up rendering, React.createElement always returns an object, which is the JSX.Element interface, but React.ReactNode is the set of all possible return values of a component.&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>JSX.Element -&amp;gt; Return value of React.createElement&lt;/li>
&lt;li>React.ReactNode -&amp;gt; Return value of a component&lt;/li>
&lt;/ul>
&lt;h2 id="type-definitions-in-antd">
&lt;a class="heading-anchor-link" href="#type-definitions-in-antd">Type Definitions in antd&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-definitions-in-antd"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding the differences, looking back at antd&amp;rsquo;s usage of these types makes more sense.
For example, the commonly used Table component uses different type definitions for the render method and renderColumnTitle. Why? Because their situations are indeed different.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-28-043512.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="render-uses-jsxelement">
&lt;a class="heading-anchor-link" href="#render-uses-jsxelement">render uses JSX.Element&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="render-uses-jsxelement"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-28-044507.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Because the table component must ultimately return a component element; it cannot be a number, string, etc.&lt;/p>
&lt;h3 id="rendercolumntitle-uses-reactreactnode">
&lt;a class="heading-anchor-link" href="#rendercolumntitle-uses-reactreactnode">renderColumnTitle uses React.ReactNode&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rendercolumntitle-uses-reactreactnode"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-28-044027.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown above, renderColumnTitle might return just a number, string, etc., not necessarily an element object.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using type definitions correctly and appropriately can prevent some bugs and also improve code readability. The extensive use of TypeScript makes me feel that types truly serve as part of the documentation.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/typescript-cheatsheets/react-typescript-cheatsheet" target="_blank" rel="noopener">react-typescript-cheatsheet&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/ant-design/ant-design" target="_blank" rel="noopener">ant-design source code&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Frontend Route Design</title><link>https://en.1991421.cn/2019/09/27/bd39266/</link><pubDate>Fri, 27 Sep 2019 22:12:50 +0800</pubDate><guid>https://en.1991421.cn/2019/09/27/bd39266/</guid><description>&lt;blockquote>
&lt;p>As the team grew, route design became chaotic. So we decided to think about it, reach a consensus with team members, and form a set of rules for everyone to follow when naming routes.&lt;/p>
&lt;/blockquote>
&lt;p>Thus, this article came into being. Here are the rules. Welcome to comment and exchange ideas.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-27-141209.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="principles">
&lt;a class="heading-anchor-link" href="#principles">Principles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="principles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>as short as possible&lt;/li>
&lt;li>easy to read&lt;/li>
&lt;li>user-friendly input when the user enters the URL&lt;/li>
&lt;/ul>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="user-list">
&lt;a class="heading-anchor-link" href="#user-list">User list&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="user-list"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/users
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="user-detail">
&lt;a class="heading-anchor-link" href="#user-detail">User detail&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="user-detail"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="o">/&lt;/span>&lt;span class="n">user&lt;/span>&lt;span class="o">/:&lt;/span>&lt;span class="n">id&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="add-user">
&lt;a class="heading-anchor-link" href="#add-user">Add user&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-user"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/user/new
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="users-functional-page">
&lt;a class="heading-anchor-link" href="#users-functional-page">User&amp;rsquo;s functional page&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="users-functional-page"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/user/:id/tel
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/55276627/web-application-api-and-front-end-routes-design" target="_blank" rel="noopener">Web application (API and Front-end) - routes design&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/hashmapinc/rest-good-practices-for-api-design-881439796dc9" target="_blank" rel="noopener">REST: Good Practices for API Design&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.ruanyifeng.com/blog/2014/05/restful_api.html" target="_blank" rel="noopener">RESTful API Design Guide&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Misunderstandings About put Operations in Redux-Saga</title><link>https://en.1991421.cn/2019/09/22/redux-sagaput/</link><pubDate>Sun, 22 Sep 2019 21:28:13 +0800</pubDate><guid>https://en.1991421.cn/2019/09/22/redux-sagaput/</guid><description>&lt;blockquote>
&lt;p>Regarding effects in saga, our normal usage seems to work fine. But yesterday during a code review, a question came up - is put action asynchronous? Such a simple question, but I couldn&amp;rsquo;t give an absolutely correct answer. True or False, I wasn&amp;rsquo;t sure. So, by reading saga source code, official documentation and testing with demos, I&amp;rsquo;ll provide an accurate answer while deepening understanding of saga.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-22-112434.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Let me start with the conclusion: it depends. If the action has no middleware processing or async blocking, then it&amp;rsquo;s synchronous. If it does, then it&amp;rsquo;s asynchronous.&lt;/p>
&lt;p>WHY? Let me elaborate!&lt;/p>
&lt;h2 id="what-is-an-effect">
&lt;a class="heading-anchor-link" href="#what-is-an-effect">What is an effect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-an-effect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>effect refers to side effect. Side effect is a concept in computer science, not original to Saga.&lt;/p>
&lt;p>What is a side effect? In computer science, a function side effect refers to when calling a function, in addition to returning the function value, it also has additional impact on the calling function. For example, modifying global variables (variables outside the function), modifying parameters, or changing external storage.&lt;/p>
&lt;/blockquote>
&lt;p>Concept from Wikipedia, &lt;a href="https://en.wikipedia.org/wiki/Side_effect_%28computer_science%29" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;p>Saga categorizes effects into many types, as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">effectTypes&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">TAKE&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;TAKE&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">PUT&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;PUT&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ALL&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;ALL‘
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> RACE: &amp;#39;&lt;/span>&lt;span class="nx">RACE&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> CALL: &amp;#39;&lt;/span>&lt;span class="nx">CALL&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> CPS: &amp;#39;&lt;/span>&lt;span class="nx">CPS&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> FORK: &amp;#39;&lt;/span>&lt;span class="nx">FORK&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> JOIN: &amp;#39;&lt;/span>&lt;span class="nx">JOIN&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> CANCEL: &amp;#39;&lt;/span>&lt;span class="nx">CANCEL&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> SELECT: &amp;#39;&lt;/span>&lt;span class="nx">SELECT&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> ACTION_CHANNEL: &amp;#39;&lt;/span>&lt;span class="nx">ACTION_CHANNEL&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> CANCELLED: &amp;#39;&lt;/span>&lt;span class="nx">CANCELLED&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> FLUSH: &amp;#39;&lt;/span>&lt;span class="nx">FLUSH&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> GET_CONTEXT: &amp;#39;&lt;/span>&lt;span class="nx">GET_CONTEXT&lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> SET_CONTEXT: &amp;#39;&lt;/span>&lt;span class="nx">SET_CONTEXT&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So, when we use Call or Put in daily development, we&amp;rsquo;re triggering different effects.&lt;/p>
&lt;h2 id="what-are-blocking-and-non-blocking-operations">
&lt;a class="heading-anchor-link" href="#what-are-blocking-and-non-blocking-operations">What are Blocking and Non-blocking Operations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-blocking-and-non-blocking-operations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Saga has this pair of concepts. What do they mean?&lt;/p>
&lt;blockquote>
&lt;p>A Blocking call means that the Saga yielded an Effect and will wait for the outcome of its execution before resuming to the next instruction inside the yielding Generator.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>A Non-blocking call means that the Saga will resume immediately after yielding the Effect.&lt;/p>
&lt;/blockquote>
&lt;p>Code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_0 code_BLOCK_0 code_BLOCK_0 code_BLOCK_0 code_0 code_BLOCK_0 code_BLOCK_0 code_0 code_BLOCK_0 code_BLOCK_0 code_0 code_0 code_BLOCK_0 code_BLOCK_0 code_0 code_0 code_BLOCK_0 code_0 code_0 code_BLOCK_0 code_0 code_BLOCK_0 code_0 code_BLOCK_0 code_0 code_0 code_0 code_BLOCK_0 code_0 code_0 code_0 code_BLOCK_0 code_0 code. _0fec code_BLOCK_0caaaaaaaaaaa.&lt;/p>
&lt;p>Official explanation, &lt;a href="https://redux-saga.js.org/docs/Glossary.html" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="is-put-operation-blocking-or-non-blocking">
&lt;a class="heading-anchor-link" href="#is-put-operation-blocking-or-non-blocking">Is put Operation Blocking or Non-blocking?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-put-operation-blocking-or-non-blocking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In effects, to dispatch an action, we use the &lt;code>put&lt;/code> function, and put is non-blocking. The blocking version is &lt;code>putResolve&lt;/code>.&lt;/p>
&lt;h3 id="what-other-operations-exist-besides-put">
&lt;a class="heading-anchor-link" href="#what-other-operations-exist-besides-put">What Other Operations Exist Besides put?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-other-operations-exist-besides-put"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Check the official website&lt;/li>
&lt;li>Look at the source code&lt;/li>
&lt;/ol>
&lt;p>I won&amp;rsquo;t go into detail here.&lt;/p>
&lt;h3 id="which-effect-functions-are-blocking-and-which-are-non-blocking">
&lt;a class="heading-anchor-link" href="#which-effect-functions-are-blocking-and-which-are-non-blocking">Which Effect Functions are Blocking and Which are Non-blocking&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="which-effect-functions-are-blocking-and-which-are-non-blocking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>By examining the saga source code, I&amp;rsquo;ve organized this [call me Lei Feng]&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">function&lt;/th>
&lt;th style="text-align: left">block&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">take&lt;/td>
&lt;td style="text-align: left">Blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">call&lt;/td>
&lt;td style="text-align: left">Blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">all&lt;/td>
&lt;td style="text-align: left">Blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">put&lt;/td>
&lt;td style="text-align: left">Non-Blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">putResolve&lt;/td>
&lt;td style="text-align: left">Blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">fork&lt;/td>
&lt;td style="text-align: left">Non-blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">cancel&lt;/td>
&lt;td style="text-align: left">Non-blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">join&lt;/td>
&lt;td style="text-align: left">Blocking&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">cps&lt;/td>
&lt;td style="text-align: left">Non-blocking&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="misunderstanding-about-non-blocking-operations">
&lt;a class="heading-anchor-link" href="#misunderstanding-about-non-blocking-operations">Misunderstanding About Non-blocking Operations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="misunderstanding-about-non-blocking-operations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After reading the above introduction, it &lt;code>seems&lt;/code> we might have a &lt;code>mature understanding&lt;/code> - is putting an action asynchronous?&lt;/p>
&lt;h4 id="example">Example&lt;/h4>&lt;p>Assume user information is initialized as null&lt;/p>
&lt;p>initState&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;age&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>effects&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">fetchUserEffects&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUserInfo&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// {&amp;#34;name&amp;#34;: &amp;#34;alan&amp;#34;,&amp;#34;age&amp;#34;: 29}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">setUserInfo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Class: fetchUserEffects, Function: fetchUserEffects, Line 8 yield select(): &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>reducers&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;USER_FETCH_SUCCEEDED&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Class: , Function: user, Line 7 (): &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As above, if it were asynchronous, it seems the user information printed in effects should be empty, however&amp;hellip;&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-22-095336.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The result is that user information is not empty, which means after yielding put an action, the reducer executes completely and updates the store before continuing with the next operation.&lt;/p>
&lt;p>&lt;code>The previous understanding of asynchronous was wrong&lt;/code>. So what does &lt;code>non-blocking&lt;/code> really mean? I checked GitHub and found someone had the same &lt;a href="https://github.com/redux-saga/redux-saga/issues/1921" target="_blank" rel="noopener">question&lt;/a>. After reading it again and again, I finally understood.&lt;/p>
&lt;ol>
&lt;li>For the above example, put dispatches an action, which is a synchronous operation. So the next store information retrieved will definitely be the latest.&lt;/li>
&lt;li>Non-blocking means that if this action has middleware or some asynchronous operations that cause store information updates to be delayed, the effects won&amp;rsquo;t wait for these operations to complete - it will continue executing the next operations.&lt;/li>
&lt;/ol>
&lt;h4 id="modifying-action-to-be-asynchronous">Modifying Action to be Asynchronous&lt;/h4>&lt;p>We introduce thunk. For specific configuration, check the official website.&lt;/p>
&lt;p>thunks&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">fetchUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">dispatch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getAddress&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dispatch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">setUserInfo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>effects&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">fetchUserEffects&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUserInfo&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fetchUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// modification point
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Class: fetchUserEffects, Function: fetchUserEffects, Line 8 yield select(): &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As above, execution shows &lt;code>user information printed in effects is empty, and effect printing executes first&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-22-130732.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="changing-put-to-putresolve">Changing put to putResolve&lt;/h4>&lt;p>According to the official documentation, putResolve is blocking, so changing put to putResolve and executing:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-23-025248.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>You&amp;rsquo;ll find &lt;code>user information printed in effects is not empty, and reducer printing executes first&lt;/code>. The difference between blocking and non-blocking is clear now.&lt;/p>
&lt;h3 id="additional-notes">
&lt;a class="heading-anchor-link" href="#additional-notes">Additional Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="additional-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>While clarifying the blocking/non-blocking issue, through component render printing and printing after put, I found that when put dispatches an action, the return status to effects is noticeably slower than what components listening to it receive.
I think this makes sense. If we separate redux and effects, when we execute an action and modify store state, redux executes its own listener functions and notifies externally, so components know first. Of course, this is just my speculation.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Gained more understanding of put operations in saga - put operations don&amp;rsquo;t necessarily complete execution before moving to the next step.&lt;/li>
&lt;li>Personal view: gradually understanding the technical details helps gradually master a technology.&lt;/li>
&lt;li>When searching for information, I always find that although there&amp;rsquo;s a lot of material, it&amp;rsquo;s often repetitive, especially in Chinese. It&amp;rsquo;s better to have your own insights. Sharing or copying provides no benefit for personal improvement.&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Side_effect_%28computer_science%29" target="_blank" rel="noopener">Side effect (computer science)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/redux-saga/redux-saga" target="_blank" rel="noopener">redux-saga&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://redux-saga.js.org/docs/Glossary.html" target="_blank" rel="noopener">redux-saga official doc&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/reduxjs/redux-thunk" target="_blank" rel="noopener">redux-thunk&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Webpack HMR Explained (Beginner's Guide)</title><link>https://en.1991421.cn/2019/09/15/webpack-hmr/</link><pubDate>Sun, 15 Sep 2019 23:21:05 +0800</pubDate><guid>https://en.1991421.cn/2019/09/15/webpack-hmr/</guid><description>&lt;blockquote>
&lt;p>Webpack has a feature called Hot Module Replacement (HMR), available since 1.x. I hadn’t focused on it much, but while improving developer experience recently, I touched this area. Summarizing it here also deepens my understanding of Webpack.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-15-140154.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="concepts">
&lt;a class="heading-anchor-link" href="#concepts">Concepts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concepts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>From the docs:&lt;/p>
&lt;blockquote>
&lt;p>Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways:&lt;/p>
&lt;ul>
&lt;li>Retain application state which is lost during a full reload.&lt;/li>
&lt;li>Save valuable development time by only updating what&amp;rsquo;s changed.&lt;/li>
&lt;li>Instantly update the browser when modifications are made to CSS/JS in the source code, which is almost comparable to changing styles directly in the browser&amp;rsquo;s dev tools.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>Without HMR, each change to HTML/JS/CSS triggers a full page reload. With HMR, only the changed modules are updated, which is faster. Full reloads also lose runtime state—for example, data stored in singletons. HMR addresses these pain points.&lt;/p>
&lt;h2 id="how-it-works">
&lt;a class="heading-anchor-link" href="#how-it-works">How It Works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-it-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Understanding the flow helps clarify HMR’s capabilities.&lt;/p>
&lt;ol>
&lt;li>You edit a source file.&lt;/li>
&lt;li>The file system detects the change and notifies Webpack.&lt;/li>
&lt;li>Webpack recompiles and notifies the HMR server.
&lt;ul>
&lt;li>Output file names carry a hash; if the source changes, the hash changes.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The HMR server notifies the HMR runtime via WebSocket; the runtime requests the updated modules.
&lt;ul>
&lt;li>Notification via WebSocket&lt;/li>
&lt;li>Fetching updated modules via HTTP&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The HMR runtime replaces the modules in the running app.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-15-151747.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="enable-hmr-in-angular">
&lt;a class="heading-anchor-link" href="#enable-hmr-in-angular">Enable HMR in Angular&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-hmr-in-angular"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official guide is detailed. See: &lt;a href="https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/configure-hmr.md" target="_blank" rel="noopener">Angular Configure Hot Module Replacement&lt;/a>&lt;/p>
&lt;ul>
&lt;li>If issues persist, check my &lt;a href="https://github.com/alanhe421/blog-admin" target="_blank" rel="noopener">example&lt;/a> for a working setup.&lt;/li>
&lt;/ul>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>HMR is a Webpack feature, not something provided or officially supported by Angular.&lt;/li>
&lt;li>Changes to the host &lt;code>index.html&lt;/code> won’t hot-reload. JS and CSS will, since in dev mode they’re loaded as modules.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-15-142429.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="enable-hmr-in-create-react-app">
&lt;a class="heading-anchor-link" href="#enable-hmr-in-create-react-app">Enable HMR in Create React App&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-hmr-in-create-react-app"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React apps are often scaffolded with Create React App, so here’s how to enable HMR there. There’s also &lt;code>react-hot-loader&lt;/code>; how does it relate to Webpack HMR?&lt;/p>
&lt;h3 id="webpack-hmr-vs-react-hot-loader">
&lt;a class="heading-anchor-link" href="#webpack-hmr-vs-react-hot-loader">Webpack HMR vs React Hot Loader&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack-hmr-vs-react-hot-loader"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>React Hot Loader is a third-party loader built on top of Webpack HMR that provides React-specific hot replacement capabilities.&lt;/li>
&lt;li>It uses Webpack’s HMR API and enables hot-swapping individual components while preserving component state.&lt;/li>
&lt;/ul>
&lt;p>So they are different things: Webpack HMR is framework-agnostic, while React Hot Loader is tailored to React.&lt;/p>
&lt;h3 id="which-to-use">
&lt;a class="heading-anchor-link" href="#which-to-use">Which to use&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="which-to-use"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since React Hot Loader is more React-centric, it’s often the more convenient choice in React apps.&lt;/p>
&lt;h3 id="setup">
&lt;a class="heading-anchor-link" href="#setup">Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>
&lt;p>Install React Hot Loader&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">npm&lt;/span> &lt;span class="n">install&lt;/span> &lt;span class="n">react&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">hot&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">loader&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I used version ^4.12.13. Note: it conflicts with &lt;code>react-router-dom@^4.3.1&lt;/code>; upgrading the router to v5 resolves it.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Enable hot loader in &lt;code>index.js&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">accept&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./routes&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">nextRoutes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./routes&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Again, depends on your project
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">ReactDOM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Provider&lt;/span> &lt;span class="nx">store&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nx">Routes&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="err">/Provider&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;root&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>}
```&lt;/p>
&lt;p>If you eject the Webpack config, you’ll notice HMR is enabled in dev; you just need to wire it up in your app.&lt;/p>
&lt;p>If you still have issues, see my example — &lt;a href="https://github.com/alanhe421/react-demo" target="_blank" rel="noopener">link&lt;/a>.&lt;/p>
&lt;h2 id="enable-hmr-in-jhipster-react">
&lt;a class="heading-anchor-link" href="#enable-hmr-in-jhipster-react">Enable HMR in JHipster React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-hmr-in-jhipster-react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>JHipster is a development platform that scaffolds modern web apps or microservice architectures. Many of my recent projects use it. Here’s how to enable HMR in the React app initialized by JHipster.&lt;/p>
&lt;p>Open the following in &lt;code>index.tsx&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hot&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">hot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">accept&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./app&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">NextApp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">require&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">{&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">typeof&lt;/span> &lt;span class="nx">AppComponent&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./app&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">NextApp&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Webpack’s HMR-related config is enabled by default.&lt;/p>
&lt;p>If you still have issues, see my example — &lt;a href="https://github.com/alanhe421/jhipster-starter" target="_blank" rel="noopener">link&lt;/a>.&lt;/p>
&lt;h2 id="enable-hmr-in-vue">
&lt;a class="heading-anchor-link" href="#enable-hmr-in-vue">Enable HMR in Vue&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-hmr-in-vue"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I don’t use Vue much, so just linking the docs:&lt;/p>
&lt;p>&lt;a href="https://vue-loader.vuejs.org/guide/hot-reload.html" target="_blank" rel="noopener">Link&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>This is a lightweight walkthrough intended to get things working.&lt;/li>
&lt;li>For more detail, read Webpack’s source.&lt;/li>
&lt;li>The techniques behind HMR or React Hot Loader are simple, but applying them to solve real problems is the valuable part. Sometimes we should use our tools to solve general problems, not just crank out repetitive business code. Agreed?&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Many of these insights come from others’ work; links below:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/configure-hmr.md" target="_blank" rel="noopener">Angular Configure Hot Module Replacement&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://webpack.js.org/concepts/hot-module-replacement/" target="_blank" rel="noopener">Webpack Hot Module Replacement&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developerhandbook.com/webpack/how-to-add-hot-module-reloading-to-a-react-app/" target="_blank" rel="noopener">How to add Hot Module Reloading (HMR) to a React app&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/30135527" target="_blank" rel="noopener">Webpack HMR vs React-Hot-Loader (Chinese)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/30669007" target="_blank" rel="noopener">Webpack HMR internals (Chinese)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.javascriptstuff.com/understanding-hmr/" target="_blank" rel="noopener">Understanding Webpack HMR&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>TSLint Does Not Catch TypeScript Errors</title><link>https://en.1991421.cn/2019/09/10/tslintcheck-typescript/</link><pubDate>Tue, 10 Sep 2019 23:04:32 +0800</pubDate><guid>https://en.1991421.cn/2019/09/10/tslintcheck-typescript/</guid><description>&lt;blockquote>
&lt;p>The title says it all, but the details matter. TSLint enforces code style—it doesn’t guarantee your TypeScript compiles. Let’s walk through an example.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-tslint-reports">
&lt;a class="heading-anchor-link" href="#what-tslint-reports">What TSLint Reports&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-tslint-reports"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-10-142254.png"
alt="lint output"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Lint errors include the violated rule. Many editors auto-fix simple issues (like quote style), so the commit diff might be empty.&lt;/p>
&lt;h2 id="type-errors-slip-through">
&lt;a class="heading-anchor-link" href="#type-errors-slip-through">Type Errors Slip Through&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-errors-slip-through"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Consider:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">interface&lt;/span> &lt;span class="nx">IStudent&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sex&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;male&amp;#39;&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="s1">&amp;#39;female&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">age&lt;/span>: &lt;span class="kt">number&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Instantiating &lt;code>IStudent&lt;/code> with only one property produces a TypeScript error in the editor, but &lt;code>git commit&lt;/code> still succeeds because TSLint is fine with it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-10-143310.png"
alt="type error"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-10-143458.png"
alt="commit succeeds"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>CI would fail later. To prevent that, run &lt;code>tsc&lt;/code> before you commit.&lt;/p>
&lt;h2 id="add-tsc-to-git-hooks">
&lt;a class="heading-anchor-link" href="#add-tsc-to-git-hooks">Add tsc to Git Hooks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-tsc-to-git-hooks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Example Husky config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hooks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pre-commit&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tsc --noEmit &amp;amp;&amp;amp; lint-staged&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pre-push&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;yarn run test&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now the commit fails until the type error is fixed.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-10-144840.png"
alt="commit blocked"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Note:&lt;/strong> &lt;code>--noEmit&lt;/code> prevents output files. This slows commits slightly but catches type regressions early—a worthwhile trade-off.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Frequent refactors introduce type mistakes. Without &lt;code>tsc&lt;/code> in the pipeline, you’ll ship broken builds. Adding it to your hooks keeps upstream code healthier.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://dev.to/robertcoopercode/comment/8o44" target="_blank" rel="noopener">Using ESLint and Prettier in a TypeScript Project&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Handling Exceptions in Redux-Saga</title><link>https://en.1991421.cn/2019/09/07/redux-saga/</link><pubDate>Sat, 07 Sep 2019 22:18:17 +0800</pubDate><guid>https://en.1991421.cn/2019/09/07/redux-saga/</guid><description>&lt;blockquote>
&lt;p>&lt;code>redux-saga&lt;/code> manages application side effects (async data fetching, browser storage, etc.). It aims to make side effects easier to manage, more efficient to run, simpler to test, and more resilient when things go wrong.&lt;/p>
&lt;p>Inevitably, effects can throw. For instance, an API call may fail. How should we handle these errors? That’s the topic here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-if-we-ignore-errors">
&lt;a class="heading-anchor-link" href="#what-if-we-ignore-errors">What If We Ignore Errors?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-if-we-ignore-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Consider an effect that calls the backend three times in sequence:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-04-141523.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>If the second &lt;code>getBooks&lt;/code> request fails, will “step 2” print?&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-04-141608.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>It doesn’t. Once the request throws, the effect aborts immediately.&lt;/p>
&lt;h2 id="handling-a-single-request">
&lt;a class="heading-anchor-link" href="#handling-a-single-request">Handling a Single Request&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-a-single-request"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Wrap the effect in &lt;code>try…catch&lt;/code>:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-11-041815.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-11-041733.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Catching the error swallows it, allowing subsequent steps to run. The console still prints &lt;code>step1&lt;/code>, &lt;code>step2&lt;/code>, and &lt;code>step3&lt;/code>.&lt;/p>
&lt;h3 id="what-counts-as-an-exception">
&lt;a class="heading-anchor-link" href="#what-counts-as-an-exception">What Counts as an Exception?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-counts-as-an-exception"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>HTTP 200, 400, 404, 500…which ones trigger &lt;code>catch&lt;/code>?&lt;/p>
&lt;p>The demo uses Axios. The key is &lt;code>validateStatus&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">validateStatus&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">validateStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">200&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">status&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">300&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">settle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">validateStatus&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">validateStatus&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">validateStatus&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">validateStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">response&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">createError&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Request failed with status code &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Responses outside &lt;code>200–299&lt;/code> fall into the &lt;code>catch&lt;/code>. 300-series responses trigger browser redirects before Axios resolves.&lt;/p>
&lt;p>Axios source: &lt;a href="https://github.com/axios/axios/blob/master/lib/defaults.js" target="_blank" rel="noopener">defaults.js&lt;/a>&lt;/p>
&lt;h2 id="preventing-a-saga-tree-crash">
&lt;a class="heading-anchor-link" href="#preventing-a-saga-tree-crash">Preventing a Saga Tree Crash&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="preventing-a-saga-tree-crash"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If a single effect throws, the watcher stops, potentially taking down the entire saga tree. To keep the app safe, wrap each effect.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">call&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;redux-saga/effects&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">safe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;[react-demo | Saga Unhandled Exception] This error should be fixed or guarded in the saga.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="apply-the-wrapper">
&lt;a class="heading-anchor-link" href="#apply-the-wrapper">Apply the Wrapper&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="apply-the-wrapper"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-03-145341.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-03-145306.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="when-an-effect-throws-the-watcher-stops">
&lt;a class="heading-anchor-link" href="#when-an-effect-throws-the-watcher-stops">When an Effect Throws, the Watcher Stops&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-an-effect-throws-the-watcher-stops"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-11-124904.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Suppose the component dispatches the same action twice:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-11-125002.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Only one log entry appears:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-11-125044.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="build-a-reusable-wrapper">
&lt;a class="heading-anchor-link" href="#build-a-reusable-wrapper">Build a Reusable Wrapper&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-a-reusable-wrapper"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To avoid repeating &lt;code>try…catch&lt;/code>, wrap the effect before registering it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">call&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;redux-saga/effects&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">safe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaFn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;[react-demo | Saga Unhandled Exception] This error should be fixed or guarded in the saga.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then wire it up:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">mySaga&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">takeEvery&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;USER_FETCH&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fetchUserEffects&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">takeEvery&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;TEST_SAGA&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">safe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">testSagaEffects&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">takeEvery&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;TEST_SAGA&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">testSagaEffects2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="result">Result&lt;/h4>&lt;p>Even if the first effect fails, the second still runs.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-03-150305.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="handling-errors-globally">
&lt;a class="heading-anchor-link" href="#handling-errors-globally">Handling Errors Globally?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-errors-globally"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Wrapping every effect works but feels repetitive. Can we automate it?&lt;/p>
&lt;p>Yes—use &lt;code>effectMiddlewares&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">effectMiddleware&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;FORK&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">effect&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">safe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">sagaMiddleware&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createSagaMiddleware&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">effectMiddlewares&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">effectMiddleware&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you don’t need to decorate each effect manually—it’s handled once.&lt;/p>
&lt;h3 id="heads-up">
&lt;a class="heading-anchor-link" href="#heads-up">Heads-Up&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="heads-up"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>effectMiddlewares&lt;/code> arrived in &lt;code>redux-saga&lt;/code> 1.0.0. Upgrade if you’re on an older version.&lt;/p>
&lt;p>Release notes: &lt;a href="https://github.com/redux-saga/redux-saga/releases/tag/v1.0.0" target="_blank" rel="noopener">v1.0.0&lt;/a>&lt;/p>
&lt;h3 id="why-fork">
&lt;a class="heading-anchor-link" href="#why-fork">Why &lt;code>FORK&lt;/code>?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-fork"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Saga effects include types such as &lt;code>TAKE&lt;/code>, &lt;code>PUT&lt;/code>, &lt;code>ALL&lt;/code>, &lt;code>FORK&lt;/code>, etc.&lt;/li>
&lt;li>&lt;code>takeEvery&lt;/code> and &lt;code>takeLatest&lt;/code> produce &lt;code>FORK&lt;/code> effects under the hood.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-14-154208.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="error-messages-feel-sparse">
&lt;a class="heading-anchor-link" href="#error-messages-feel-sparse">Error Messages Feel Sparse?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-messages-feel-sparse"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Yes—the logs only show the action name, not the exact code location inside the effect.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://i.imgur.com/trTgd6f.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Wrapping effects keeps watchers alive, but ask why the error happens. Hiding it doesn’t make the app safer. Often it’s better to fix the root cause.&lt;/li>
&lt;li>Libraries like saga are powerful, but every abstraction comes with trade-offs. Learn the internals and build custom helpers when needed.&lt;/li>
&lt;/ol>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/redux-saga/redux-saga" target="_blank" rel="noopener">redux-saga GitHub&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://restfulapi.net/http-status-codes/" target="_blank" rel="noopener">HTTP Status Codes&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/33821692" target="_blank" rel="noopener">HTTP requests explained via comics&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/axios/axios" target="_blank" rel="noopener">axios GitHub&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Implementing Less and CSS Modules in Projects</title><link>https://en.1991421.cn/2019/09/01/lesscss-modules/</link><pubDate>Sun, 01 Sep 2019 22:46:00 +0800</pubDate><guid>https://en.1991421.cn/2019/09/01/lesscss-modules/</guid><description>&lt;blockquote>
&lt;p>Recently, I took over a frontend project that uses Less and CSS modules for styling. Here I&amp;rsquo;ll organize and summarize these approaches.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-26-145228.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Before showing the actual code, it&amp;rsquo;s necessary to understand the background of both technologies.&lt;/p>
&lt;h2 id="what-is-less">
&lt;a class="heading-anchor-link" href="#what-is-less">What is Less?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-less"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We know that the three essential elements of frontend development are CSS, HTML, and JavaScript. CSS handles styling, HTML handles content, and JS controls interaction. This is fundamental.&lt;/p>
&lt;p>But we also know that CSS is too rigid—you could say it only deals with static values. We&amp;rsquo;re used to object-oriented programming and formulas, and even for CSS, we want to follow the DRY principle. So, can we enhance CSS? That&amp;rsquo;s where LESS comes in.&lt;/p>
&lt;blockquote>
&lt;p>Less (which stands for Leaner Style Sheets) is a backwards-compatible language extension for CSS. This is the official documentation for Less, the language and Less.js, the JavaScript tool that converts your Less styles to CSS styles.&lt;/p>
&lt;/blockquote>
&lt;p>In short, LESS is a CSS preprocessor. Besides LESS, there&amp;rsquo;s also SCSS, but they&amp;rsquo;re quite similar. In the end, I decided to use LESS in the current project.&lt;/p>
&lt;p>There are two reasons:&lt;/p>
&lt;ol>
&lt;li>For our UI component library, we chose Antd, which uses LESS. Maintaining consistency with it helps us better control the overall UI.&lt;/li>
&lt;li>In terms of syntax, I&amp;rsquo;m more used to Less.&lt;/li>
&lt;/ol>
&lt;h2 id="advantages-of-less">
&lt;a class="heading-anchor-link" href="#advantages-of-less">Advantages of Less&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="advantages-of-less"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The advantages of Less address the pain points of CSS itself. As mentioned above, using Less allows us to better reuse CSS through variables, formulas, etc.&lt;/p>
&lt;h2 id="what-are-css-modules">
&lt;a class="heading-anchor-link" href="#what-are-css-modules">What are CSS Modules?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-css-modules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. All URLs (url(&amp;hellip;)) and @imports are in module request format (./xxx and ../xxx means relative, xxx and xxx/yyy means in modules folder, i. e. in node_modules).&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>In a nutshell, a CSS module is a CSS file where all class names and animation names have their own scope.&lt;/code>&lt;/p>
&lt;h3 id="a-brief-mention-of-my-previous-frontend-project">
&lt;a class="heading-anchor-link" href="#a-brief-mention-of-my-previous-frontend-project">A Brief Mention of My Previous Frontend Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="a-brief-mention-of-my-previous-frontend-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Let me talk about my previous project. We used Less but not CSS modules. There was no distinction between local and global scopes for styles, and the naming conventions were all over the place. The serious consequence was that we had many !important declarations and a lot of &amp;ldquo;robbing Peter to pay Paul&amp;rdquo; fixes. The styles weren&amp;rsquo;t well-planned or designed, with zero reusability. From a pessimistic view, I&amp;rsquo;d say the maintainability was also zero.&lt;/p>
&lt;/blockquote>
&lt;p>How to solve this? After much reflection, I decided to use CSS Modules.&lt;/p>
&lt;h2 id="the-code">
&lt;a class="heading-anchor-link" href="#the-code">The Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="project-technical-background">
&lt;a class="heading-anchor-link" href="#project-technical-background">Project Technical Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-technical-background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>TypeScript &lt;code>~3.3.1&lt;/code>&lt;/li>
&lt;li>React &lt;code>16.4.2&lt;/code>&lt;/li>
&lt;li>Webpack &lt;code>4.17.1&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Now that we understand the background, let&amp;rsquo;s start.&lt;/p>
&lt;h2 id="typingsdts">
&lt;a class="heading-anchor-link" href="#typingsdts">typings.d.ts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typingsdts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To support Less as a module import in TS components, we need to make a declaration:&lt;/p>
&lt;p>typings.d.ts&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">declare&lt;/span> &lt;span class="nx">module&lt;/span> &lt;span class="s1">&amp;#39;*.less&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="webpackjs">
&lt;a class="heading-anchor-link" href="#webpackjs">Webpack.js&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpackjs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We need to add corresponding rules in webpack:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">test&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sr">/\.less$/&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">use&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">require&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;style-loader&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;css-loader&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">importLoaders&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">localsConvention&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;camelCase&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">modules&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">mode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;local&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">localIdentName&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;[name]-[local]-[hash:base64:5]&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sourceMap&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s1">&amp;#39;production&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loader&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;less-loader&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">options&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">javascriptEnabled&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sourceMap&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="s1">&amp;#39;production&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A few things to note:&lt;/p>
&lt;ol>
&lt;li>The sourceMap configuration is for easy debugging of Less in the development environment.&lt;/li>
&lt;li>CSS module is enabled in css-loader, not less-loader.&lt;/li>
&lt;li>For the naming convention in localIdentName, I used kebab-case (though snake_case would also work).&lt;/li>
&lt;/ol>
&lt;h2 id="writing-styles-in-react-projects">
&lt;a class="heading-anchor-link" href="#writing-styles-in-react-projects">Writing Styles in React Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="writing-styles-in-react-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="local">
&lt;a class="heading-anchor-link" href="#local">Local&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="local"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>footer.less&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">.footer {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> background-color: rgb(0, 0, 0);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> color: #999999;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> padding: 49px 155px 70px;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> font-size: 14px;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> letter-spacing: 0;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> text-align: left;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> line-height: 18px;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-01-134344.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="final-effect">Final Effect&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-02-115014.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note:&lt;/p>
&lt;ul>
&lt;li>If you use kebab-case in Less, like .footer-bg, it will still be camelCase in the component (footerBg)&lt;/li>
&lt;li>I personally recommend using camelCase for CSS modular styles to ensure consistency between components and Less files. For non-modular styles (those we want to be global), use kebab-case to distinguish between the two.&lt;/li>
&lt;/ul>
&lt;h3 id="global">
&lt;a class="heading-anchor-link" href="#global">Global&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="global"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To write global styles, just add the :global selector. After specifying the global scope with :global, the compilation won&amp;rsquo;t add hash fingerprints.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">global&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">.table-tip&lt;/span> &lt;span class="err">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="o">@&lt;/span>&lt;span class="k">font&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">bold&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="o">@&lt;/span>&lt;span class="kc">blue&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">letter-spacing&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">text-align&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">center&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="o">@&lt;/span>&lt;span class="n">light-blue&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border-radius&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding-top&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding-bottom&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">16&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">padding-left&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">22&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Global styles can be used normally in all components, simply by using className directly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">className&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;table-tip&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">FormattedMessage&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">{&amp;#39;tip&amp;#39;}&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="final-effect-1">Final Effect&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-02-114926.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="overall-style-planning-and-design">
&lt;a class="heading-anchor-link" href="#overall-style-planning-and-design">Overall Style Planning and Design&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="overall-style-planning-and-design"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With Less and CSS modules in place, I did some planning and design for the project&amp;rsquo;s overall style.&lt;/p>
&lt;h3 id="theme">
&lt;a class="heading-anchor-link" href="#theme">Theme&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="theme"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In the app project, I created a separate folder called theme:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── antd.less
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── company.less
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── theme.less
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>antd.less is used to rewrite default styles, like button colors, form sizes and spacing, etc.&lt;/li>
&lt;li>theme.less defines font variables, the website&amp;rsquo;s basic color scheme, etc.&lt;/li>
&lt;li>company.less exists because I considered extracting common styles into a company-wide unified UI style during project development. Since multiple company projects follow a unified style, extracting this makes sense. By syncing the entire theme folder in the future, we&amp;rsquo;d have the basic UI ready. It&amp;rsquo;s about DRY. If you&amp;rsquo;re not concerned with reusing project styles, this file can be removed.&lt;/li>
&lt;/ul>
&lt;h3 id="appless">
&lt;a class="heading-anchor-link" href="#appless">app.less&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="appless"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This is used to define global styles and also imports the antd styles from the theme folder, overriding default styles when loading. It also imports theme variables.&lt;/p>
&lt;h3 id="componentless">
&lt;a class="heading-anchor-link" href="#componentless">component.less&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="componentless"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>With global styles and a series of global variables defined, individual components only need to create their own customized styles as needed. For common values like fonts and colors, they can simply import theme.less.&lt;/p>
&lt;h2 id="style-modularization-in-angular">
&lt;a class="heading-anchor-link" href="#style-modularization-in-angular">Style Modularization in Angular&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="style-modularization-in-angular"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Since I&amp;rsquo;ve worked with Angular 2 before, which is a framework with a complete set of solutions, let&amp;rsquo;s briefly discuss how it handles style modularization.&lt;/p>
&lt;p>&lt;code>Note: When we talk about Angular here, we're referring to Angular 2 and later versions. The latest version is currently 9.0.0-next.4.&lt;/code>&lt;/p>
&lt;p>Here&amp;rsquo;s a component style I wrote and its final parsed result:&lt;/p>
&lt;p>home.component.css&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">h1&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="nt">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;say hello&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">red&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-01-140741.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Looking at this, you should understand that it essentially uses CSS attribute selectors to achieve CSS modularization. Comparing it with the class name approach of CSS modules, you&amp;rsquo;ll find they&amp;rsquo;re similar in principle.&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Even without using CSS Modules, methodologies like BEM could solve the problem. But undeniably, the CSS Modules approach is more elegant. With the setup described above, our styles won&amp;rsquo;t have unexpected effects or become a mess.&lt;/p>
&lt;p>Of course, good tools are one thing, but what&amp;rsquo;s more important is the team using those tools and forming a unified understanding. So, keep going!&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/css-modules/css-modules" target="_blank" rel="noopener">https://github.com/css-modules/css-modules&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/23571898" target="_blank" rel="noopener">Introduction to CSS Modules I: What is it? Why use it?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/20495964" target="_blank" rel="noopener">CSS Modules Modularization Solution&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://css-tricks.com/bem-101/" target="_blank" rel="noopener">BEM&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://angular.io/guide/component-styles" target="_blank" rel="noopener">Angular Component Styles&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841" target="_blank" rel="noopener">Case Styles: Camel, Pascal, Snake, and Kebab Case&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Home Synology NAS DDNS Setup</title><link>https://en.1991421.cn/2019/08/25/nas-ddns/</link><pubDate>Sun, 25 Aug 2019 14:48:51 +0800</pubDate><guid>https://en.1991421.cn/2019/08/25/nas-ddns/</guid><description>&lt;blockquote>
&lt;p>Purchasing Synology comes with QuickConnect service, which allows direct external network access, but the access is very slow [heard it goes through Taiwan] and the domain name isn&amp;rsquo;t great. To make it faster, you need to set up custom domain binding.&lt;/p>
&lt;/blockquote>
&lt;p>Those familiar with DDNS should know this is to solve the dynamic IP domain binding problem. To learn about DDNS, &lt;a href="https://www.lifewire.com/definition-of-dynamic-dns-816294" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h2 id="having-public-ip">
&lt;a class="heading-anchor-link" href="#having-public-ip">Having Public IP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="having-public-ip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Taking Beijing Unicom broadband as an example, it doesn&amp;rsquo;t have &lt;code>public IP&lt;/code> by default, you need to contact customer service to enable it&lt;/p>
&lt;blockquote>
&lt;p>Contact customer service 10010, press 0 for manual service, ask the agent to transfer to broadband business line, then request to enable public IP.&lt;/p>
&lt;p>Note: DDNS is for scenarios with public IP but not fixed IP service, so fixed IP is not needed here. Unicom broadband fixed IP requires separate payment of 100 CNY/month. After customer service enables public IP, you need to restart the modem/router for it to take effect.&lt;/p>
&lt;p>Testing found that Unicom&amp;rsquo;s public IP enablement doesn&amp;rsquo;t support IPv6, at least I didn&amp;rsquo;t get it, but having IPv4 already meets the requirements.&lt;/p>
&lt;/blockquote>
&lt;h2 id="domain-name">
&lt;a class="heading-anchor-link" href="#domain-name">Domain Name&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="domain-name"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Direct IP access is still not user-friendly, so a domain name is needed here. I&amp;rsquo;m using Tencent Cloud domain.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-25-062743.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-25-062808.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="router-port-forwarding">
&lt;a class="heading-anchor-link" href="#router-port-forwarding">Router Port Forwarding&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="router-port-forwarding"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When accessing enters the gateway, it needs to be forwarded to NAS service based on port&lt;/p>
&lt;h2 id="domain-resolution">
&lt;a class="heading-anchor-link" href="#domain-resolution">Domain Resolution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="domain-resolution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, I choose to use dnspod here, add an A record for nas,&lt;/p>
&lt;h2 id="getting-api-token-from-dnspod">
&lt;a class="heading-anchor-link" href="#getting-api-token-from-dnspod">Getting API Token from dnspod&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-api-token-from-dnspod"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-25-063651.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Record the id and token, these are the username and password&lt;/p>
&lt;h2 id="modifying-dns-resolution-address-in-alibaba-cloud-formerly-wanwang">
&lt;a class="heading-anchor-link" href="#modifying-dns-resolution-address-in-alibaba-cloud-formerly-wanwang">Modifying DNS Resolution Address in Alibaba Cloud (formerly Wanwang)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="modifying-dns-resolution-address-in-alibaba-cloud-formerly-wanwang"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Click into the corresponding domain name, modify according to the addresses provided by dnspod&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-25-063534.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="configuring-ddns-on-nas">
&lt;a class="heading-anchor-link" href="#configuring-ddns-on-nas">Configuring DDNS on NAS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuring-ddns-on-nas"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Control Panel&lt;/li>
&lt;li>External Access&lt;/li>
&lt;li>DDNS, select DNSPod.cn, fill in username and password, confirm&lt;/li>
&lt;/ul>
&lt;h2 id="router-port-forwarding-1">
&lt;a class="heading-anchor-link" href="#router-port-forwarding-1">Router Port Forwarding&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="router-port-forwarding-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I&amp;rsquo;m using Xiaomi Router 3 here&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-25-064115.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="potential-issues">
&lt;a class="heading-anchor-link" href="#potential-issues">Potential Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="potential-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>DNS Resolution Timeliness&lt;/p>
&lt;p>If you just bought a new domain from Alibaba Cloud, you need to upload ID documents for review, which takes time, so resolution cannot take effect immediately&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Baidu IP shows public IP but still doesn&amp;rsquo;t work
Because what we need here is an IP that can uniquely identify your address, specifically a public and unique IP. It&amp;rsquo;s possible your entire residential area shares only one public IP, so Baidu will also show public IP, but it&amp;rsquo;s not unique, so it still won&amp;rsquo;t work.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After all these settings, it should work. The access address would be &lt;code>http://nas.xxx.cn:5000&lt;/code>. Of course, I didn&amp;rsquo;t ultimately succeed because I don&amp;rsquo;t have a true public IP - the so-called public IP is shared by the entire residential area. How to solve this problem?
Look into intranet penetration. I ultimately solved it using intranet penetration.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.moks.cc/?p=9" target="_blank" rel="noopener">Setting up DDNS with your own domain to access Synology externally&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.95408.com/blog/3348.html" target="_blank" rel="noopener">Using Xiaomi Router DDNS function + dynamic public IP for external access&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>IDEA Polishing Journey - Live Templates</title><link>https://en.1991421.cn/2019/08/20/idea-live-template/</link><pubDate>Tue, 20 Aug 2019 23:18:00 +0800</pubDate><guid>https://en.1991421.cn/2019/08/20/idea-live-template/</guid><description>&lt;blockquote>
&lt;p>IDEA&amp;rsquo;s success is undeniable - by 2018, its market share had grown to over 50%. What makes it so good? From a developer&amp;rsquo;s perspective, it&amp;rsquo;s incredibly thoughtful. Many designs and small features genuinely solve pain points and improve efficiency.
Recently, I spent some time researching and polishing one of these features - Live Templates. Here&amp;rsquo;s a brief summary and sharing of my experience.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-are-live-templates">
&lt;a class="heading-anchor-link" href="#what-are-live-templates">What are Live Templates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-live-templates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>By using live templates, you can insert frequently-used constructions into your code. For example, loops, conditions, various declarations, or print statements.&lt;/p>
&lt;/blockquote>
&lt;p>In simple terms, &lt;code>live templates&lt;/code> allow you to quickly insert commonly used code blocks.&lt;/p>
&lt;h2 id="demo-time">
&lt;a class="heading-anchor-link" href="#demo-time">Demo Time&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="demo-time"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-ScreenRecording2019-08-21at00051.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The GIF shows a component class that I frequently need in my current frontend project, which connects to Redux and intl. Writing this manually each time felt like a waste of time. So, I turned it into a template.&lt;/p>
&lt;p>How to do it? Keep reading.&lt;/p>
&lt;h2 id="adding-live-templates">
&lt;a class="heading-anchor-link" href="#adding-live-templates">Adding Live Templates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-live-templates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="open-settings--live-templates">
&lt;a class="heading-anchor-link" href="#open-settings--live-templates">Open Settings → Live Templates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-settings--live-templates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-153402.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>&lt;code>Open Settings with shortcut: Command + Shift + A&lt;/code>&lt;/em>&lt;/p>
&lt;h3 id="add-live-templates">
&lt;a class="heading-anchor-link" href="#add-live-templates">Add Live Templates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-live-templates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-153518.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="field-explanations">Field Explanations&lt;/h4>&lt;ul>
&lt;li>abbreviation: The shorthand text that triggers the template when typed&lt;/li>
&lt;li>description: The description shown by the IDE to help understand the template&amp;rsquo;s purpose&lt;/li>
&lt;li>template: The actual code block we want to insert, including specified variables&lt;/li>
&lt;/ul>
&lt;h3 id="scope-configuration">
&lt;a class="heading-anchor-link" href="#scope-configuration">Scope Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scope-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This determines in which environments the IDE will suggest this template when typing the abbreviation. For example, here I only want it in TypeScript, so I select only TS.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-154006.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="adding-variables">
&lt;a class="heading-anchor-link" href="#adding-variables">Adding Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The reason it&amp;rsquo;s called &amp;ldquo;live template&amp;rdquo; is because of variables. Here I only control the component name.&lt;/p>
&lt;h4 id="predefined-variables">Predefined Variables&lt;/h4>&lt;p>Currently there are two: &lt;code>$END$&lt;/code> and &lt;code>$SELECTION$&lt;/code>. &lt;code>$END$&lt;/code> controls the cursor position, while &lt;code>$SELECTION$&lt;/code> represents the selected code block.&lt;/p>
&lt;p>&lt;a href="https://www.jetbrains.com/help/idea/template-variables.html" target="_blank" rel="noopener">Official documentation - click here&lt;/a>&lt;/p>
&lt;h4 id="expressions">Expressions&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-153554.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note that expressions can be nested. For example, here I want the component name to be the filename without extension, with the first letter capitalized.&lt;/p>
&lt;h4 id="skip-if-defined">Skip if defined&lt;/h4>&lt;p>Let me explain what this means. Literally, it means &amp;ldquo;skip if defined&amp;rdquo;. If we check this, the cursor will skip this position and move to the next one; otherwise, it will stop here for editing mode.&lt;/p>
&lt;p>If I don&amp;rsquo;t select this here, even if we have &lt;code>$END$&lt;/code> in the defaults, when generating the template, the cursor will default to the &lt;code>$ComponentName$&lt;/code> position.&lt;/p>
&lt;h4 id="variable-default-values">Variable Default Values&lt;/h4>&lt;p>As the name suggests, these are the default values when we haven&amp;rsquo;t defined them. But there&amp;rsquo;s a trick here - we can define a default value as another variable value.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-163400.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The image shows what I set when creating a console template. The benefit of this is that we can have linked requirements.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-163530.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>For example, when we edit one variable value, another one will change accordingly. Here&amp;rsquo;s the effect:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-20-ScreenRecording2019-08-21at00371.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After setting up the variables, the template is complete. Click OK, and it&amp;rsquo;s ready to use.&lt;/p>
&lt;h2 id="live-template-installation">
&lt;a class="heading-anchor-link" href="#live-template-installation">Live Template Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="live-template-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Besides creating your own, you can install templates created by others. Currently, I&amp;rsquo;ve found that JAR method doesn&amp;rsquo;t work, but XML works.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>If the file format is XML, download it directly&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Find the live template directory. For example, my local path is &lt;code>~/Library/Application Support/JetBrains/WebStorm2021.2/jba_config/templates&lt;/code>. Note that the path may vary depending on the IDE version. Copy the downloaded XML file into this directory&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Restart the IDE, and the installation is complete&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A simple LiveTemplate is now complete. When coding, LiveTemplates can significantly improve our productivity. I believe we should focus on two things:&lt;/p>
&lt;ol>
&lt;li>Familiarize yourself with the default LiveTemplates in your IDE. These are gems summarized from practical experience by predecessors, excellent in both flexibility and naming. For example, &lt;code>sout&lt;/code> and &lt;code>psvm&lt;/code> in Java, or &lt;code>importdefault&lt;/code> and &lt;code>importitems&lt;/code> in JavaScript.&lt;/li>
&lt;li>Based on your project situation and coding habits, extract your commonly used patterns into templates to fill the gaps in defaults. For example, I&amp;rsquo;ve extracted frontend UT cases, console.log statements, arrow functions, etc.&lt;/li>
&lt;/ol>
&lt;p>Any repetitive work becomes manual labor, and we need to identify these manual tasks, abstract them, and formalize them. Live templates are an excellent solution that saves us time. So, give it a try!&lt;/p></description></item><item><title>Type Casting vs. Type Assertions in TypeScript</title><link>https://en.1991421.cn/2019/08/18/typescript/</link><pubDate>Sun, 18 Aug 2019 16:33:21 +0800</pubDate><guid>https://en.1991421.cn/2019/08/18/typescript/</guid><description>&lt;blockquote>
&lt;p>A teammate asked why casting a form value &lt;code>as A&lt;/code> didn’t remove extra fields. The question exposed a gap in my own understanding, so I dug in. Here’s what I found.&lt;/p>
&lt;/blockquote>
&lt;h2 id="reproducing-the-issue">
&lt;a class="heading-anchor-link" href="#reproducing-the-issue">Reproducing the Issue&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reproducing-the-issue"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We declare type &lt;code>A&lt;/code> with four properties but the form returns five. Casting with &lt;code>as A&lt;/code> doesn’t strip the extra field:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-18-074625.png"
alt="example code"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-18-074307.png"
alt="console output"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;code>height&lt;/code> sticks around. Deleting the property manually works, but &lt;em>why&lt;/em>?&lt;/p>
&lt;h2 id="no-runtime-casting-in-typescript">
&lt;a class="heading-anchor-link" href="#no-runtime-casting-in-typescript">No Runtime Casting in TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="no-runtime-casting-in-typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript is a superset of JavaScript. There’s no runtime type conversion—only &lt;strong>type assertions&lt;/strong> and &lt;strong>type inference&lt;/strong>. &lt;code>as Foo&lt;/code> and &lt;code>&amp;lt;Foo&amp;gt;&lt;/code> are assertions. They do not change the runtime object.&lt;/p>
&lt;h3 id="type-assertions">
&lt;a class="heading-anchor-link" href="#type-assertions">Type Assertions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-assertions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">foo&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">bar&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">foo&lt;/span> &lt;span class="kr">as&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// bar is typed as string
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In JSX/TSX, prefer &lt;code>as&lt;/code> syntax to avoid conflicts with JSX tags.&lt;/p>
&lt;p>Assertions tell the compiler what you, the developer, believe the type to be. They don’t alter runtime data.&lt;/p>
&lt;h2 id="type-conversions">
&lt;a class="heading-anchor-link" href="#type-conversions">Type Conversions?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-conversions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you actually need to remove properties, you must do it manually (e.g., copy only allowed fields). TypeScript won’t do it for you.&lt;/p>
&lt;h2 id="type-inference">
&lt;a class="heading-anchor-link" href="#type-inference">Type Inference&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-inference"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript infers types when possible (variable declarations, return values, destructuring, etc.). It reduces boilerplate, but for complex cases I still recommend explicit annotations.&lt;/p>
&lt;h2 id="how-typescript-compiles">
&lt;a class="heading-anchor-link" href="#how-typescript-compiles">How TypeScript Compiles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-typescript-compiles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript adds a type system but ultimately emits JavaScript. The diagram from “Programming TypeScript” illustrates the pipeline:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-18-141566115980_.pic.jpg"
alt="compiler pipeline"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="contrast-with-java-casting">
&lt;a class="heading-anchor-link" href="#contrast-with-java-casting">Contrast with Java Casting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="contrast-with-java-casting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Java performs runtime casts (widening/narrowing). For example, casting a subclass to a parent can hide properties.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-18-142340.png"
alt="java example"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>TypeScript, being JavaScript-based, behaves differently.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript isn’t Java; keep its model in mind. Understand assertions vs. conversions, embrace type inference responsibly, and manipulate runtime data explicitly when needed.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://jkchao.github.io/typescript-book-chinese" target="_blank" rel="noopener">Deep Dive into TypeScript (Chinese)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.tslang.cn/docs/handbook/basic-types.html" target="_blank" rel="noopener">TypeScript Handbook&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/41528375/tyepscript-cast-object-to-parent-object" target="_blank" rel="noopener">StackOverflow: cast object to parent object&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use Web Fonts in Frontend (Step-by-Step Guide)</title><link>https://en.1991421.cn/2019/08/11/frontend-web-font/</link><pubDate>Sun, 11 Aug 2019 11:48:21 +0800</pubDate><guid>https://en.1991421.cn/2019/08/11/frontend-web-font/</guid><description>&lt;blockquote>
&lt;p>I recently picked up a web project and, after getting the mockups from the designer, spent a few days planning and tuning the styles. The design uses custom fonts instead of the system defaults—&lt;code>lato-regular&lt;/code> and &lt;code>lato-bold&lt;/code>—so I needed to set up the font imports. It’s not complicated, but I’d never organized my thoughts on it, so I dug into some references and pulled together what follows.&lt;/p>
&lt;/blockquote>
&lt;h2 id="web-font-basics">
&lt;a class="heading-anchor-link" href="#web-font-basics">Web Font Basics&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web-font-basics"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="the-font-family-property">
&lt;a class="heading-anchor-link" href="#the-font-family-property">The &lt;code>font-family&lt;/code> Property&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-font-family-property"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When we control fonts on a webpage, we usually use &lt;code>font-family&lt;/code> or the shorthand &lt;code>font&lt;/code> property. CSS describes &lt;code>font-family&lt;/code> like this:&lt;/p>
&lt;blockquote>
&lt;p>The font-family CSS property specifies a prioritized list of one or more font family names and/or generic family names for the selected element.&lt;/p>
&lt;/blockquote>
&lt;p>In other words, the browser will try the fonts we specify in that order for the target element.&lt;/p>
&lt;h4 id="how-font-selection-works">How Font Selection Works&lt;/h4>&lt;p>Once we declare the fonts, the browser asks the operating system for them. If the OS doesn’t have the first font installed, the browser moves on to the next one in the list. If none of the listed fonts exist locally, the browser falls back to its default font.&lt;/p>
&lt;p>So, to keep the appearance consistent, always end the list with a few fonts that you know are widely available on every system.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-09-161335.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">medium&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">content&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">sans-serif&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">font&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">apple-system&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlinkMacSystemFont&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Segoe UI&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Roboto&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Oxygen&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Ubuntu&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Cantarell&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Open Sans&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Helvetica Neue&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">sans-serif&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is how Medium defines the font stack for the &lt;code>a&lt;/code> element. The first several entries are custom or platform-specific fonts, and the final &lt;code>sans-serif&lt;/code> is the universal fallback.&lt;/p>
&lt;h3 id="the-frontend-stack--where-web-fonts-sit">
&lt;a class="heading-anchor-link" href="#the-frontend-stack--where-web-fonts-sit">The Frontend Stack &amp;amp; Where Web Fonts Sit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-frontend-stack--where-web-fonts-sit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When I first started building frontends, people constantly repeated the “three pillars” mantra: HTML, CSS, and JavaScript.&lt;/p>
&lt;p>So where do web fonts live? In CSS. Obvious, maybe, but keep it in mind.&lt;/p>
&lt;h2 id="importing-custom-fonts">
&lt;a class="heading-anchor-link" href="#importing-custom-fonts">Importing Custom Fonts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="importing-custom-fonts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="import-methods">
&lt;a class="heading-anchor-link" href="#import-methods">Import Methods&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="import-methods"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The three frontend pillars give us three load approaches.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;code>@import&lt;/code>&lt;/p>
&lt;p>We use Less as our CSS preprocessor, and this is how we import custom fonts within it:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-09-161644.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Notes&lt;/em>&lt;/p>
&lt;ul>
&lt;li>The URL can be relative or absolute. Cross-origin addresses need the right access control.&lt;/li>
&lt;li>&lt;code>@font-face&lt;/code> is a CSS3 feature, and browsers support different font file types. Internet Explorer 9, Firefox, Opera, Chrome, and Safari all support the &lt;code>@font-face&lt;/code> rule. IE9 only understands &lt;code>.eot&lt;/code>; Firefox, Chrome, Safari, and Opera support &lt;code>.ttf&lt;/code> and &lt;code>.otf&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Remember: Internet Explorer 8 and older versions do not support &lt;code>@font-face&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Standard &lt;code>&amp;lt;link&amp;gt;&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;link rel='stylesheet' type='text/css' href='http://fonts.googleapis.com/css?family=Condiment'>
```
3. JavaScript
JavaScript can manipulate HTML elements, so we can inject a `link` tag dynamically.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">head&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">innerHTML&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="s1">&amp;#39;&amp;lt;link rel=&amp;#34;stylesheet&amp;#34; type=&amp;#34;text/css&amp;#34; href=&amp;#34;http://fonts.googleapis.com/css?family=Condiment&amp;#34;&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Remember that once the browser adds those CSS or JS resources, it will immediately load and execute them.&lt;/p>
&lt;h2 id="icon-fonts">
&lt;a class="heading-anchor-link" href="#icon-fonts">Icon Fonts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="icon-fonts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s pretty much everything about font configuration. Let’s branch out to icon fonts, which are widely used now. Icons started as raster images, but today icon fonts and SVGs are usually better options.&lt;/p>
&lt;p>Here’s how icon fonts work:&lt;/p>
&lt;ol>
&lt;li>Design the icons in SVG format.&lt;/li>
&lt;li>Convert the icons into a font file.&lt;/li>
&lt;li>Use semantic CSS classes and pseudo-element &lt;code>content&lt;/code> to map Unicode glyphs to each icon.&lt;/li>
&lt;/ol>
&lt;p>Because we’re dealing with vector graphics, there’s no quality loss, and we don’t have to micromanage image assets like we used to.&lt;/p>
&lt;p>The downside is obvious, though: icon fonts are either monochrome or gradient only, so keep that in mind.&lt;/p>
&lt;h3 id="recommended-icon-font-options">
&lt;a class="heading-anchor-link" href="#recommended-icon-font-options">Recommended Icon Font Options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-icon-font-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Font Awesome and Iconfont are the two services I rely on most when I need a third-party icon set.&lt;/p>
&lt;p>Each has pros and cons. Font Awesome provides an entire collection with consistent styling, but that also means less flexibility and a relatively large payload. Iconfont works like Taobao: add what you like to your “shopping cart” and export the bundle. Which one should you choose? Let your project requirements decide.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Years ago, I was blown away by the visual polish of Apple’s website and puzzled by why their typography looked better than other sites. Once I became a web developer, I realized they were loading a custom font—PingFang. Time flies; customized fonts are commonplace today. They do add weight to your frontend assets, but you can mitigate the latency through CDN, gzip, HTTP/2, and other optimizations. User experience should always move forward, not backward, so frontend work is ultimately about presenting better services and experiences.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-11-034634.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="further-reading">
&lt;a class="heading-anchor-link" href="#further-reading">Further Reading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="further-reading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.ibm.com/developerworks/cn/web/1505_zhangyan_googlefont/index.html" target="_blank" rel="noopener">Use Google Fonts to Bring Better Type to Your Site&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zh.wikipedia.org/zh-hans/%E8%8B%B9%E6%96%B9" target="_blank" rel="noopener">PingFang Typeface&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/47737638" target="_blank" rel="noopener">How Icon Fonts Are Made and Used&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Jest Less Import Error</title><link>https://en.1991421.cn/2019/08/04/jestless/</link><pubDate>Sun, 04 Aug 2019 21:22:24 +0800</pubDate><guid>https://en.1991421.cn/2019/08/04/jestless/</guid><description>&lt;blockquote>
&lt;p>Recently encountered UT errors in a frontend project. At first I was completely confused, but gradually unraveled the issue through careful analysis. Documenting this here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="error-phenomenon">
&lt;a class="heading-anchor-link" href="#error-phenomenon">Error Phenomenon&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-phenomenon"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-04-121718.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="error-code">
&lt;a class="heading-anchor-link" href="#error-code">Error CODE&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">Test suite failed to run
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Jest encountered an unexpected token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> This usually means that you are trying to import a file which Jest cannot parse, e.g. it&lt;span class="s1">&amp;#39;s not plain JavaScript.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> By default, if Jest sees a Babel config, it will use that to transform your files, ignoring &amp;#34;node_modules&amp;#34;.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> Here&amp;#39;&lt;/span>s what you can &lt;span class="k">do&lt;/span>:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> • To have some of your &lt;span class="s2">&amp;#34;node_modules&amp;#34;&lt;/span> files transformed, you can specify a custom &lt;span class="s2">&amp;#34;transformIgnorePatterns&amp;#34;&lt;/span> in your config.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> • If you need a custom transformation specify a &lt;span class="s2">&amp;#34;transform&amp;#34;&lt;/span> option in your config.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> • If you simply want to mock your non-JS modules &lt;span class="o">(&lt;/span>e.g. binary assets&lt;span class="o">)&lt;/span> you can stub them out with the &lt;span class="s2">&amp;#34;moduleNameMapper&amp;#34;&lt;/span> config option.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> You&lt;span class="s1">&amp;#39;ll find more details and examples of these config options in the docs:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> https://jestjs.io/docs/en/configuration.html
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> Details:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> /xxx/xx/B.less:1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> ({&amp;#34;Object.&amp;lt;anonymous&amp;gt;&amp;#34;:function(module,exports,require,__dirname,__filename,global,jest){.searchRow {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> ^
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> SyntaxError: Unexpected token .
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> 31 |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> 32 |
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> &amp;gt; 33 | import styles from &amp;#39;&lt;/span>app/xxx/xx/B.less&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span> ^
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="initial-analysis">
&lt;a class="heading-anchor-link" href="#initial-analysis">Initial Analysis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="initial-analysis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Why does a specific component&amp;rsquo;s LESS file cause issues&lt;/p>
&lt;p>This error seemed strange because many components import LESS files, so why did only this one fail? At first I thought there might be some invisible corrupted characters or spaces in the LESS file that weren&amp;rsquo;t visible to the naked eye. But after reformatting and checking, I couldn&amp;rsquo;t find anything.&lt;/p>
&lt;p>So I compared it with other components and found one difference with the failing LESS file - it was imported by two components. For example, component A has style A.less, component A itself imports style A.less, and component B also imports A.less. I quickly copied B.less out and reran UT - it passed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Why did the Main component UT fail&lt;/p>
&lt;p>Since it was confirmed to be a component LESS import issue, this part was easier to analyze. The Main component imports route configuration, and the route configuration file imports major components. So when UT runs, it executes these imports and thus reports the error.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Runtime OK but UT not OK - Jest configuration?
Since runtime doesn&amp;rsquo;t report errors, it means the code is fine. So I looked at the current jest configuration and had an epiphany:&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-04-125119.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;pre>&lt;code>Modules imported via absolute app paths don't go through `identity-obj-proxy`, and the failing line was an app path import, so the LESS wasn't successfully mocked.
I tried adding the following rule:
&lt;/code>&lt;/pre>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-04-130129.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;pre>&lt;code>Reran UT - it passed.
&lt;/code>&lt;/pre>
&lt;h2 id="best-solution">
&lt;a class="heading-anchor-link" href="#best-solution">Best Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="best-solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Although the above configuration addition can solve the problem, what&amp;rsquo;s the best solution? This is worth considering.&lt;/p>
&lt;p>The above issue reveals that the current Jest configuration does have shortcomings. If you import LESS from app paths, UT will fail. But realistically speaking, is there currently a need to import LESS from absolute app paths? &lt;code>No&lt;/code>.&lt;/p>
&lt;p>Because as shown above, it actually imports duplicate LESS files. Just in terms of bundle size, it essentially packages duplicate LESS files. Since there&amp;rsquo;s CSS module, the two don&amp;rsquo;t interfere with each other. But since the goal is to reuse this style, the current issue is just using the wrong approach.&lt;/p>
&lt;h3 id="correct-approach">
&lt;a class="heading-anchor-link" href="#correct-approach">Correct Approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="correct-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If you need to reuse such LESS:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>One method is to put it in the root component&amp;rsquo;s index.less, where CSS is global.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If not placed there, it can be placed at the common parent component level of both components. For example, if these two components are component A and component B, they have a common parent component C, so put the style on C. Then A and B can naturally reuse it.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="related-knowledge-points-summary">
&lt;a class="heading-anchor-link" href="#related-knowledge-points-summary">Related Knowledge Points Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-knowledge-points-summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>CSS Modularization&lt;/li>
&lt;/ol>
&lt;p>CSS modularization is now a very mature CSS style management solution. Why call it management? Because with it, it actually solves the long-standing annoying CSS conflict issues. At the same time, CSS modularization also provides an outlet for global styles. This makes management quite elegant.&lt;/p>
&lt;ol start="2">
&lt;li>import styles from &amp;lsquo;index.less&amp;rsquo;&lt;/li>
&lt;/ol>
&lt;p>This import method turns CSS usage into JS usage. So if we move positions or rename CSS, it can be automatically modified. This unifies CSS and JS. Isn&amp;rsquo;t that great.&lt;/p>
&lt;ol start="3">
&lt;li>&lt;code>identity-obj-proxy&lt;/code> in jest&lt;/li>
&lt;/ol>
&lt;p>This module is designed to solve the use of non-JS resources in our UT tested components, such as CSS, PNG, SVG, etc. In testing, many things need to be mocked. Tests always aim to check our core logic with the lowest fabrication cost and most realistic testing points.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>At this point, this problem is solved and understood. Happy. There are still many pitfalls in frontend development. But all methods follow the same principles. Let&amp;rsquo;s encourage each other.&lt;/p></description></item><item><title>Frontend React Project Structure and Conventions</title><link>https://en.1991421.cn/2019/07/28/react/</link><pubDate>Sun, 28 Jul 2019 21:05:55 +0800</pubDate><guid>https://en.1991421.cn/2019/07/28/react/</guid><description>&lt;blockquote>
&lt;p>After working with React for over a year, I&amp;rsquo;ve gained some insights into React practices. While designing the frontend architecture for a new project, I reflected on the shortcomings and issues of previous projects and decided to improve and simplify the structure this time.
I also researched mature designs and open-source projects, ultimately deciding to adopt the following structure.&lt;/p>
&lt;/blockquote>
&lt;p>Welcome to discuss and collaborate for mutual improvement.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-07-28-134612.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="directory-planning">
&lt;a class="heading-anchor-link" href="#directory-planning">Directory Planning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="directory-planning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">├── app
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── action-types
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── actions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── api
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── app.less
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── app.tsx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── components
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── effects
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── index.tsx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── reducers
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── routes.tsx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── shared
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── types
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── typings.d.ts
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── webpack
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── favicon.ico
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── i18n
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── index.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── manifest.webapp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── mock-data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── robots.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── static
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── images
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── .editorconfig
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── .huskyrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── .prettierrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── tsconfig.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── tsconfig.test.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── tslint.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── yarn.lock
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="thoughts-behind-the-structure">
&lt;a class="heading-anchor-link" href="#thoughts-behind-the-structure">Thoughts Behind the Structure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="thoughts-behind-the-structure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The structure above might seem empty and simple, but behind it are some of my current thoughts. Let me highlight them here.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>React is a library, not a framework, so it&amp;rsquo;s very flexible. This is its charm because you can use it however you want. But projects aren&amp;rsquo;t always personal projects - we sometimes need to establish boundaries to ensure the project&amp;rsquo;s organizational structure isn&amp;rsquo;t chaotic. So basic scaffolding and conventions are indeed necessary.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Since React itself isn&amp;rsquo;t a framework, there are no official suggestions or structural requirements. When working on actual projects, we need to determine a reasonable architecture based on the technology stack we adopt and actual business development scenarios.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The projects I&amp;rsquo;ve participated in use Redux. As we all know, the official recommendation is to split components into Components and Containers - presentational components and container components. I think this concept is correct. But in actual practice, I found that this separation can to some extent cause strongly related code to be forcibly separated. So in this architecture, I don&amp;rsquo;t plan to separate them into different folders. After all, they&amp;rsquo;re still React components, so they can all go directly under the components folder. As for internal separation of whether to connect to Redux, that&amp;rsquo;s optional.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>editorConfig + tslint + husky safeguard basic code style and quality&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>&lt;em>Note:&lt;/em> There&amp;rsquo;s no best, only better. In upcoming practices, I&amp;rsquo;ll continue to improve this based on actual situations and discovered issues. Welcome feedback from fellow developers.&lt;/p>
&lt;h2 id="some-conventions">
&lt;a class="heading-anchor-link" href="#some-conventions">Some Conventions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="some-conventions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Besides the basic structure above, I&amp;rsquo;ve established the following conventions for the team:&lt;/p>
&lt;ul>
&lt;li>Folder names: lowercase, kebab-case naming, e.g., user-comment&lt;/li>
&lt;li>Component file class names: lowercase kebab-case naming&lt;/li>
&lt;li>Route-level component naming: add &lt;code>page&lt;/code> suffix&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I worked with Angular in 2018 and before, only starting to work with React in the past year due to project needs. Although there are syntactic differences, and one is a library while the other is a framework - admittedly, there are indeed many differences, and I was somewhat uncomfortable at first. But as I gradually delved deeper, there were actually many familiar points, like hooks, component communication, routing, etc. Until one day, I had an epiphany - yes, all methods lead to the same destination.&lt;/p>
&lt;p>Learning technology is about learning thoughts and content. Don&amp;rsquo;t be deceived by superficial tricks. Let&amp;rsquo;s encourage each other.&lt;/p></description></item><item><title>Splitting Frontend i18n Files</title><link>https://en.1991421.cn/2019/07/24/frontend-i18n-file-splitting/</link><pubDate>Wed, 24 Jul 2019 22:33:00 +0800</pubDate><guid>https://en.1991421.cn/2019/07/24/frontend-i18n-file-splitting/</guid><description>&lt;blockquote>
&lt;p>Static texts (titles, dialog messages, etc.) need i18n. As content grows, a single file becomes unwieldy. Split by feature into multiple JSON/JS files to improve maintainability.&lt;/p>
&lt;/blockquote>
&lt;p>We want the split JSON files to look like this:
&lt;figure class="image-figure">
&lt;img
src="https://user-images.githubusercontent.com/9245110/61802090-7d13fa00-ae62-11e9-9478-aeb8ab29b99f.png"
alt="image"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>How do we achieve this? Keep reading.&lt;/p>
&lt;h2 id="environment">
&lt;a class="heading-anchor-link" href="#environment">Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here’s the environment I used:&lt;/p>
&lt;ul>
&lt;li>React &lt;code>v16.4.2&lt;/code>&lt;/li>
&lt;li>React-intl &lt;code>v^2.7.2&lt;/code>&lt;/li>
&lt;li>Webpack &lt;code>v4.17.1&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="install-merge-jsons-webpack-plugin">
&lt;a class="heading-anchor-link" href="#install-merge-jsons-webpack-plugin">Install merge-jsons-webpack-plugin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-merge-jsons-webpack-plugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">yarn add merge-jsons-webpack-plugin --dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="webpack-config">
&lt;a class="heading-anchor-link" href="#webpack-config">Webpack config&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="webpack-config"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">MergeJsonWebpackPlugin&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;merge-jsons-webpack-plugin&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">new&lt;/span> &lt;span class="nx">MergeJsonWebpackPlugin&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">output&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">groupBy&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">pattern&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;./src/main/webapp/i18n/en/*.json&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">fileName&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;./i18n/en.json&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="app-loading">
&lt;a class="heading-anchor-link" href="#app-loading">App loading&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="app-loading"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>The following is the React loading code; Angular, Vue, etc. follow the same pattern.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">getLocale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">lang&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`/i18n/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">lang&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">.json`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">setupAxiosInterceptors&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/#/login&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">rootEl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;root&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">messages&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">locale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;en&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">render&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ReactDOM&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">AppContainer&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">IntlProvider&lt;/span> &lt;span class="na">locale&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">locale&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="na">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">messages&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">locale&lt;/span>&lt;span class="p">]}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// @ts-ignore
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">Provider&lt;/span> &lt;span class="na">store&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nx">store&lt;/span>&lt;span class="p">}&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">AppComponent&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">Provider&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">IntlProvider&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">AppContainer&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rootEl&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">getLocale&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">locale&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">locale&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">render&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="how-it-works">
&lt;a class="heading-anchor-link" href="#how-it-works">How it works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-it-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The concept is simple, but let’s walk through it anyway: Webpack, as the build tool, merges N JSON files into one and moves it to a specific path. The web app requests the merged file, so externally it’s still a single file, while authors can maintain multiple files. Webpack handles that middle step.&lt;/p>
&lt;h2 id="caching-of-merged-files">
&lt;a class="heading-anchor-link" href="#caching-of-merged-files">Caching of merged files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="caching-of-merged-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We all know the frontend caching story: for static assets such as images, videos, or i18n files, the browser may stick with the cached version. If we add translations but users keep seeing the cached file, what should we do? We need a way to make the browser skip the cache and fetch the fresh file.&lt;/p>
&lt;h3 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add a timestamp parameter to the request. Changing the URL means the browser naturally bypasses the cache.
In effect, whenever the frontend ships a new build, users pick up the new i18n file; otherwise they keep using the cached one.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">getLocale&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">lang&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`/i18n/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">lang&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">.json`&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">params&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">buildTimestamp&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">BUILD_TIMESTAMP&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="whats-the-best-approach">
&lt;a class="heading-anchor-link" href="#whats-the-best-approach">What’s the best approach?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="whats-the-best-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>That works, but let’s go a step further.&lt;/p>
&lt;p>Strictly speaking, “build time” correlates with i18n changes but isn’t a sufficient condition. The perfect solution is: if the i18n content changes, request the new file; otherwise stay with the cached one.&lt;/p>
&lt;p>Can we make that happen? Definitely—it’s all just code. MergeJsonWebpackPlugin would need to add a hash fingerprint during the merge (just like JS bundling), and the request would target the hashed filename. The plugin doesn’t support it yet, so it’s on us to implement.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s it for now—no new pitfalls uncovered yet.
A small problem, but a foundational one. Bye!&lt;/p></description></item><item><title>Upgrading Angular 7 to 8</title><link>https://en.1991421.cn/2019/06/16/angular78/</link><pubDate>Sun, 16 Jun 2019 20:58:51 +0800</pubDate><guid>https://en.1991421.cn/2019/06/16/angular78/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-06-16-130414.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Angular 8 was officially released on May 28, 2019. Since my personal blog platform is on Angular 7, I took some time to upgrade and keep up with the technology.&lt;/p>
&lt;p>&lt;code>Note: Angular now updates major versions every six months. The last version 7 was released in October last year.&lt;/code>&lt;/p>
&lt;h2 id="what-are-the-main-changes-in-angular-8">
&lt;a class="heading-anchor-link" href="#what-are-the-main-changes-in-angular-8">What are the main changes in Angular 8?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-the-main-changes-in-angular-8"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Differential Loading [Different browsers can now load different JS files]&lt;/li>
&lt;li>Route Lazy Loading Implemented with Dynamic Imports&lt;/li>
&lt;li>CLI Workflow Improvements&lt;/li>
&lt;li>About Ivy and Bazel, &lt;code>Ivy is the new rendering engine. Currently, Angular's bundle size and performance are still quite criticized, especially compared to React and Vue. So everyone is waiting for this Ivy, hoping to be able to really use it soon.&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="personal-opinion">
&lt;a class="heading-anchor-link" href="#personal-opinion">Personal Opinion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="personal-opinion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Version 8 has few noteworthy features. After all, it&amp;rsquo;s an even-numbered version, which is mainly about strengthening and refining rather than major changes. The long-awaited Ivy still isn&amp;rsquo;t ready, so we&amp;rsquo;ll have to wait. But keeping up with versions isn&amp;rsquo;t a bad thing.&lt;/p>
&lt;p>Enough talk, let&amp;rsquo;s look at the upgrade.&lt;/p>
&lt;h2 id="version-upgrade">
&lt;a class="heading-anchor-link" href="#version-upgrade">Version Upgrade&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="version-upgrade"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here are the version changes before and after my successful upgrade:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-06-16-131352.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Note&lt;/em>&lt;/p>
&lt;ul>
&lt;li>TypeScript needs to be upgraded to &lt;code>3.4&lt;/code>, as stated on the official website&lt;/li>
&lt;li>Node needs to be upgraded to &lt;code>10&lt;/code>. For actual projects, it&amp;rsquo;s recommended to create a &lt;code>.nvmrc&lt;/code> configuration file in the root directory to control the project&amp;rsquo;s Node version&lt;/li>
&lt;/ul>
&lt;h2 id="size-changes">
&lt;a class="heading-anchor-link" href="#size-changes">Size Changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="size-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I&amp;rsquo;m afraid this might disappoint everyone - there&amp;rsquo;s currently no significant change in bundle size.&lt;/p>
&lt;h3 id="ng7">
&lt;a class="heading-anchor-link" href="#ng7">NG7&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ng7"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-06-16-258929F3-FDF4-4B54-BEB3-349484550A55.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="ng8">
&lt;a class="heading-anchor-link" href="#ng8">NG8&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ng8"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-06-16-BF729A55-1FC5-4926-A560-630F5304BB1F.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Note&lt;/em>&lt;/p>
&lt;ul>
&lt;li>There&amp;rsquo;s an additional polyfill-es5 file here, which should be the differential loading feature mentioned above. After all, different browsers have different levels of JavaScript support.&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The upgrade is quite simple with no major pitfalls. However, while upgrading, understanding why these improvements and designs are made is very meaningful. Think about it when you have time.&lt;/p>
&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/angular/angular/blob/master/CHANGELOG.md" target="_blank" rel="noopener">https://github.com/angular/angular/blob/master/CHANGELOG.md&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://alligator.io/angular/angular-8/" target="_blank" rel="noopener">https://alligator.io/angular/angular-8/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>IDEA Plugin Development</title><link>https://en.1991421.cn/2019/06/02/idea-plugin/</link><pubDate>Sun, 02 Jun 2019 20:40:41 +0800</pubDate><guid>https://en.1991421.cn/2019/06/02/idea-plugin/</guid><description>&lt;blockquote>
&lt;p>Every weekday morning we conduct CodeReview sessions. The general process involves a group of people looking at a shared screen, opening each project, reviewing commits, and recording problematic code by noting line numbers, file names, project names, and issues. However, the recording process is time-consuming and error-prone. The need to record actually reduces the efficiency of CodeReview. We&amp;rsquo;ve made several attempts to improve CodeReview efficiency.&lt;/p>
&lt;p>Initially, one person operated the IDE to browse code while another recorded notes, but the recorder often struggled to keep up, resulting in incomplete records such as missing code locations and issues.
Later, we tried using IDE bookmarks for marking, but this approach had limitations: it didn&amp;rsquo;t support cross-project functionality (each project was independent) and lacked export capabilities. Our output typically needed to be recorded in Confluence.&lt;/p>
&lt;/blockquote>
&lt;p>As shown above, the recording and follow-up process was inefficient and time-consuming. So I thought, why not develop an enhanced version of bookmarks? I&amp;rsquo;ll implement those missing features myself!&lt;/p>
&lt;h2 id="feasibility">
&lt;a class="heading-anchor-link" href="#feasibility">Feasibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="feasibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If we&amp;rsquo;re going to do this, we need to consider feasibility and outline the required functionality:&lt;/p>
&lt;ol>
&lt;li>Record target line numbers, project names, and file paths&lt;/li>
&lt;li>Application-level [multi-project] sharing&lt;/li>
&lt;li>Persistence across IDE restarts (closely related to point 2, essentially persistent storage)&lt;/li>
&lt;li>Set system clipboard content&lt;/li>
&lt;li>Keyboard shortcut binding support&lt;/li>
&lt;/ol>
&lt;h2 id="sdk-and-third-party-research">
&lt;a class="heading-anchor-link" href="#sdk-and-third-party-research">SDK and Third-party Research&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sdk-and-third-party-research"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After reviewing official documentation and mature plugin projects, I confirmed that file system operations, system clipboard access, persistent storage, and keyboard shortcut binding are all supported. This confirmed that the project was feasible.&lt;/p>
&lt;p>Here I&amp;rsquo;d like to comment on two characteristics of Chinese blog articles: 1) Excessive reposting with lack of original content - if you&amp;rsquo;re going to repost, at least include the original link and properly format images (some layouts are completely messed up); 2) Lack of substantive content - good articles don&amp;rsquo;t necessarily need to be long, but they should focus on key points and explain things clearly. Well, we still have work to do.&lt;/p>
&lt;h2 id="environment-setup">
&lt;a class="heading-anchor-link" href="#environment-setup">Environment Setup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-setup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>IDEA provides excellent support for plugin development (well, it&amp;rsquo;s their own platform after all). Click &amp;ldquo;New Project&amp;rdquo;:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-06-02-125248.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Click &amp;ldquo;Next&amp;rdquo; and complete the setup.&lt;/p>
&lt;h2 id="project-structure">
&lt;a class="heading-anchor-link" href="#project-structure">Project Structure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-structure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The initialized project is quite basic. Here&amp;rsquo;s my project structure:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">├── README.md
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── bookmarker4idea.iml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── out
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── resources
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   └── META-INF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│   └── plugin.xml // Configuration file
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── src // Source code
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── cn
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │   └── alanhe
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │   ├── BookMarkX.java
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │   ├── BookMarkXPersistentStateComponent.java
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │   ├── BookmarkXItemState.java
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │   └── CopyBookMarkX.java
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── icon
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │   └── bookmarker.png // Icons and static resources
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── test // Tests
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="pluginxml">
&lt;a class="heading-anchor-link" href="#pluginxml">plugin.xml&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pluginxml"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Development mainly consists of two parts: Java and XML. We need to configure menus, icons, events, and persistent storage in this file.
Here&amp;rsquo;s part of my plugin configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;idea-plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>cn.alanhe.plugin.bookmarkx4idea&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;name&amp;gt;&lt;/span>bookmarkx4idea&lt;span class="nt">&amp;lt;/name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>1.0&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;vendor&lt;/span> &lt;span class="na">email=&lt;/span>&lt;span class="s">&amp;#34;i@alanhe.me&amp;#34;&lt;/span> &lt;span class="na">url=&lt;/span>&lt;span class="s">&amp;#34;https://github.com/alanhe421/bookmarkex4idea&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>personal&lt;span class="nt">&amp;lt;/vendor&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;idea-version&lt;/span> &lt;span class="na">since-build=&lt;/span>&lt;span class="s">&amp;#34;173.0&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> on how to target different products --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- uncomment to enable plugin in all products--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;depends&amp;gt;&lt;/span>com.intellij.modules.lang&lt;span class="nt">&amp;lt;/depends&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;extensions&lt;/span> &lt;span class="na">defaultExtensionNs=&lt;/span>&lt;span class="s">&amp;#34;com.intellij&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;applicationService&lt;/span> &lt;span class="na">serviceImplementation=&lt;/span>&lt;span class="s">&amp;#34;cn.alanhe.BookMarkXPersistentStateComponent&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/extensions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;actions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Add your actions here --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;action&lt;/span> &lt;span class="na">id=&lt;/span>&lt;span class="s">&amp;#34;bookmark&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;cn.alanhe.BookMarkX&amp;#34;&lt;/span> &lt;span class="na">text=&lt;/span>&lt;span class="s">&amp;#34;toggle BookMarkx&amp;#34;&lt;/span> &lt;span class="na">description=&lt;/span>&lt;span class="s">&amp;#34;bookmark&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;add-to-group&lt;/span> &lt;span class="na">group-id=&lt;/span>&lt;span class="s">&amp;#34;Bookmarks&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;keyboard-shortcut&lt;/span> &lt;span class="na">keymap=&lt;/span>&lt;span class="s">&amp;#34;$default&amp;#34;&lt;/span> &lt;span class="na">first-keystroke=&lt;/span>&lt;span class="s">&amp;#34;ctrl alt B&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/action&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;action&lt;/span> &lt;span class="na">id=&lt;/span>&lt;span class="s">&amp;#34;ExportBookMark&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;cn.alanhe.CopyBookMarkX&amp;#34;&lt;/span> &lt;span class="na">text=&lt;/span>&lt;span class="s">&amp;#34;Copy BookMark to Clipboard&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">icon=&lt;/span>&lt;span class="s">&amp;#34;/icon/bookmarker.png&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">description=&lt;/span>&lt;span class="s">&amp;#34;Copy BookMark to Clipboard&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;add-to-group&lt;/span> &lt;span class="na">group-id=&lt;/span>&lt;span class="s">&amp;#34;EditorPopupMenu&amp;#34;&lt;/span> &lt;span class="na">anchor=&lt;/span>&lt;span class="s">&amp;#34;first&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/action&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/actions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/idea-plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://www.jetbrains.org/intellij/sdk/docs/welcome.html" target="_blank" rel="noopener">http://www.jetbrains.org/intellij/sdk/docs/welcome.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://corochann.com/intellij-plugin-development-introduction-persiststatecomponent-903.html" target="_blank" rel="noopener">https://corochann.com/intellij-plugin-development-introduction-persiststatecomponent-903.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://intellij-support.jetbrains.com/hc/en-us/community/posts/360001787320-Create-VirtualFile-or-PsiFile-from-content?page=1#community_comment_360000524140" target="_blank" rel="noopener">https://intellij-support.jetbrains.com/hc/en-us/community/posts/360001787320-Create-VirtualFile-or-PsiFile-from-content?page=1#community_comment_360000524140&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://programandonet.com/questions/90284/intellij-idea-plugin-persistentstatecomponent-loadstate-no-se-llama" target="_blank" rel="noopener">http://programandonet.com/questions/90284/intellij-idea-plugin-persistentstatecomponent-loadstate-no-se-llama&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Developing an IDEA plugin proved to be a rewarding experience that addressed a real pain point in our daily workflow. The process demonstrated how custom tooling can significantly improve team productivity by automating repetitive tasks and providing better integration with existing development environments.&lt;/p>
&lt;p>The key takeaways from this project include the importance of thorough research before implementation, understanding the IDEA plugin architecture, and leveraging the extensive SDK documentation provided by JetBrains. While the initial setup and configuration might seem complex, the structured approach and well-documented APIs make plugin development quite manageable.&lt;/p>
&lt;p>This bookmark enhancement plugin not only solved our specific CodeReview efficiency issues but also opened up possibilities for creating other custom tools tailored to our development workflow. The ability to extend IDE functionality is a powerful capability that every development team should consider exploring to optimize their processes.&lt;/p></description></item><item><title>Upgrading Angular 6 to 7</title><link>https://en.1991421.cn/2019/05/26/angular67/</link><pubDate>Sun, 26 May 2019 17:20:56 +0800</pubDate><guid>https://en.1991421.cn/2019/05/26/angular67/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-05-26-092129.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Angular 7 was officially released on &lt;a href="https://github.com/angular/angular/blob/master/CHANGELOG.md" target="_blank" rel="noopener">October 18, 2018&lt;/a>. It&amp;rsquo;s been half a year since then - if we don&amp;rsquo;t keep up, we&amp;rsquo;ll fall behind.&lt;/p>
&lt;p>Since my personal blog platform is built on Angular 6, I took this opportunity to upgrade and learn about version 7.&lt;/p>
&lt;p>Note that Angular&amp;rsquo;s current latest version is 8.0.0-rc5. Please check the official website for the latest version information.&lt;/p>
&lt;h2 id="what-are-the-main-changes-in-angular-7">
&lt;a class="heading-anchor-link" href="#what-are-the-main-changes-in-angular-7">What are the main changes in Angular 7?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-the-main-changes-in-angular-7"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>CLI Prompts&lt;/li>
&lt;li>Application Performance&lt;/li>
&lt;li>Material CDK&lt;/li>
&lt;li>Improved Select Accessibility&lt;/li>
&lt;li>Angular Elements&lt;/li>
&lt;li>Ecosystem Improvements [Angular Console, @angular/fire, NativeScript, StackBlitz]&lt;/li>
&lt;li>Dependency Updates [TypeScript 3.1, RxJS 6.3, Node 10]&lt;/li>
&lt;/ol>
&lt;p>For a quick overview, check out the official blog&amp;rsquo;s &lt;a href="https://blog.angular.io/version-7-of-angular-cli-prompts-virtual-scroll-drag-and-drop-and-more-c594e22e7b8c" target="_blank" rel="noopener">version announcement&lt;/a>. For specific API usage, refer to the documentation.&lt;/p>
&lt;h3 id="personal-opinion">
&lt;a class="heading-anchor-link" href="#personal-opinion">Personal Opinion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="personal-opinion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I think these changes aren&amp;rsquo;t particularly significant. Angular&amp;rsquo;s current version management strategy is such that even-numbered versions are the major releases, while odd-numbered ones are transitional. The anticipated Ivy renderer still hasn&amp;rsquo;t been released yet - we&amp;rsquo;ll have to wait.&lt;/p>
&lt;p>Enough talk, let&amp;rsquo;s look at the upgrade process.&lt;/p>
&lt;h2 id="version-upgrade">
&lt;a class="heading-anchor-link" href="#version-upgrade">Version Upgrade&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="version-upgrade"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s the direct comparison of versions before and after my successful upgrade.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-05-26-085831.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;em>Note&lt;/em>&lt;/p>
&lt;ul>
&lt;li>Upgrading core-js to version 3 will cause errors, so I&amp;rsquo;m keeping it at 2.x&lt;/li>
&lt;/ul>
&lt;p>After only changing the versions of these packages, both running and building worked without issues - it was quite smooth.&lt;/p>
&lt;h2 id="size-changes">
&lt;a class="heading-anchor-link" href="#size-changes">Size Changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="size-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Bundle size in frontend development is always worth paying attention to, as smaller sizes mean faster loading speeds. Here are screenshots of the bundle sizes before and after the upgrade. Since the current project is small, the size changes aren&amp;rsquo;t very obvious, but you can still see that the main bundle got smaller by approximately &lt;code>18%&lt;/code>.&lt;/p>
&lt;h3 id="before-upgrade---v6">
&lt;a class="heading-anchor-link" href="#before-upgrade---v6">Before Upgrade - v6&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="before-upgrade---v6"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-05-26-084034.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="after-upgrade---v7">
&lt;a class="heading-anchor-link" href="#after-upgrade---v7">After Upgrade - v7&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="after-upgrade---v7"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-05-26-085639.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="official-upgrade-guide">
&lt;a class="heading-anchor-link" href="#official-upgrade-guide">Official Upgrade Guide&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="official-upgrade-guide"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As I mentioned in previous blog posts, the official team maintains an upgrade guide. Of course, this is quite rough, and following it exactly can still lead to issues.
I&amp;rsquo;m just providing it here for reference:&lt;/p>
&lt;p>&lt;a href="https://update.angular.io/" target="_blank" rel="noopener">https://update.angular.io/&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Overall, upgrading from Angular 6 to 7 was relatively smooth. The main benefits observed were a reduction in bundle size and some performance improvements. While the changes in version 7 weren&amp;rsquo;t groundbreaking, keeping up with the latest versions ensures access to bug fixes, security updates, and gradual performance improvements.&lt;/p></description></item><item><title>Automating Blog Platform Deployment</title><link>https://en.1991421.cn/2019/05/26/blog-platform-auto-deployment/</link><pubDate>Sun, 26 May 2019 15:56:18 +0800</pubDate><guid>https://en.1991421.cn/2019/05/26/blog-platform-auto-deployment/</guid><description>&lt;blockquote>
&lt;p>I used to build locally and FTP to a VPS. That manual loop wastes time, so I set up automated deployment.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tech-stack">
&lt;a class="heading-anchor-link" href="#tech-stack">Tech Stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tech-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Code hosted on GitHub&lt;/li>
&lt;li>Frontend: Angular&lt;/li>
&lt;li>Backend: Express&lt;/li>
&lt;li>VPS: Tencent Cloud server (CentOS 7.x)&lt;/li>
&lt;/ul>
&lt;p>I used &lt;code>Travis&lt;/code> for CI/CD.&lt;/p>
&lt;h2 id="deployment-flow">
&lt;a class="heading-anchor-link" href="#deployment-flow">Deployment flow&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="deployment-flow"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-05-26-133146.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After code is committed to GitHub, Travis builds and packages it, then executes a sync script to transfer files to the VPS, and finally runs a service restart script.&lt;/p>
&lt;h2 id="key-points">
&lt;a class="heading-anchor-link" href="#key-points">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The entire configuration process isn&amp;rsquo;t difficult, but there are a few pitfalls to watch out for.&lt;/p>
&lt;h3 id="file-transfer">
&lt;a class="heading-anchor-link" href="#file-transfer">File transfer&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="file-transfer"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Use &lt;code>rsync&lt;/code> to sync artifacts to the server.&lt;/p>
&lt;p>For detailed command documentation, &lt;a href="http://man.linuxde.net/rsync" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="ssh-passwordless-login">
&lt;a class="heading-anchor-link" href="#ssh-passwordless-login">SSH Passwordless Login&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ssh-passwordless-login"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Travis needs to transfer packaged files to the VPS, which requires username/password for remote login. Plain text passwords have security issues, but Travis provides encryption. Here&amp;rsquo;s how to set it up:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>SSH login to the VPS (commands 2-3 are executed on the target VPS)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Create public/private key pair for Travis&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ ssh-keygen -f travis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Encrypt the private key and create instructions in YML&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ gem install travis
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ travis login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ ssh-keygen -t rsa -b &lt;span class="m">4096&lt;/span> -C &lt;span class="s1">&amp;#39;qianghe421@163.com&amp;#39;&lt;/span> -f ./deploy_rsa
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ travis encrypt-file deploy_rsa --add
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ ssh-copy-id -i deploy_rsa.pub root@1991421.cn
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>After executing the commands, the YML file will create the script for step 4 below&lt;/p>
&lt;pre>&lt;code>*Note*
&lt;/code>&lt;/pre>
&lt;p>My VPS runs CentOS 7, and I got errors when executing gem, so I had to troubleshoot&lt;/p>
&lt;pre>&lt;code>#### Installing RubyGems
```bash
yum install rubygems
yum install ruby-devel
yum install make automake gcc gcc-c++ kernel-devel
```
/usr/bin/ssh-copy-id: ERROR: Host key verification failed.
```bash
$ ssh-copy-id -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /var/www/blog/alanhe421.github.io/deploy_rsa.pub root@1991421.cn
```
&lt;/code>&lt;/pre>
&lt;ol start="4">
&lt;li>
&lt;p>Modify the script&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">- openssl aes-256-cbc -K &lt;span class="nv">$encrypted_b86c2ca78306_key&lt;/span> -iv &lt;span class="nv">$encrypted_b86c2ca78306_iv&lt;/span> -in .travis/travis.enc -out ~/.ssh/id_rsa -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The automatically generated script needs a small modification&lt;/p>
&lt;p>&lt;em>Note&lt;/em>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>When the script is created, it may wrap automatically. Make sure it&amp;rsquo;s on a single line; otherwise Travis will fail with &lt;code>Could not parse .travis.yml&lt;/code>&lt;/li>
&lt;/ul>
&lt;ol start="5">
&lt;li>Add ssh_known_hosts&lt;/li>
&lt;/ol>
&lt;p>Without this, you&amp;rsquo;ll get the following connection prompt&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-08-030501.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="complete-script-configuration">
&lt;a class="heading-anchor-link" href="#complete-script-configuration">Complete Script Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="complete-script-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">language&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">node_js&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">node_js&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">8&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">addons&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ssh_known_hosts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="m">132.232.104.101&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">before_install&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">openssl aes-256-cbc -K $encrypted_b86c2ca78306_key -iv $encrypted_b86c2ca78306_iv -in .travis/travis.enc -out ~/.ssh/id_rsa -d&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">chmod 600 ~/.ssh/id_rsa&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">install&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd backend&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">npm install&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ..&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd frontend&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">npm install&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ..&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd frontend&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">npm run build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ..&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd backend&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">npm run build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ..&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">after_script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ./frontend/dist/blog-admin&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">rsync -az -vv --delete -e &amp;#39;ssh -p 22&amp;#39; ./** root@132.232.104.101:/var/www/blog-admin/dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ../../..&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ./backend/dist&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">rsync -az -vv --delete -e &amp;#39;ssh -p 22&amp;#39; ./** root@132.232.104.101:/var/www/blog-admin&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">cd ../..&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">rsync -az -vv --delete -e &amp;#39;ssh -p 22&amp;#39; backend/package*.json root@132.232.104.101:/var/www/blog-admin&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">ssh -p 22 root@132.232.104.101 &amp;#34;cd /var/www/blog-admin;npm install --production;pm2 restart blog-admin&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">only&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">master&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">notifications&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">email&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">i@alanhe.me&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After implementing CI automatic deployment, future changes to the blog platform just need to be committed to GitHub - everything else is automated. Perfect!&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://juejin.im/post/5c6e8327518825620a7f2389" target="_blank" rel="noopener">Automating Vue Code Deployment with Travis CI and SSH&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://uedsky.com/2016-06/travis-deploy/" target="_blank" rel="noopener">https://uedsky.com/2016-06/travis-deploy/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.travis-ci.com/" target="_blank" rel="noopener">https://docs.travis-ci.com/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://my.oschina.net/binxin/blog/651565" target="_blank" rel="noopener">SSH Passwordless Login: Principles and Configuration&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>[Translation] Evaluating Redux Saga Test Libraries</title><link>https://en.1991421.cn/2019/05/19/redux-saga/</link><pubDate>Sun, 19 May 2019 22:30:38 +0800</pubDate><guid>https://en.1991421.cn/2019/05/19/redux-saga/</guid><description>&lt;p>Original post: &lt;a href="https://blog.scottlogic.com/2018/01/16/evaluating-redux-saga-test-libraries.html" target="_blank" rel="noopener">Evaluating Redux Saga Test Libraries&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>We recently adopted redux-saga to address complex async flows. I found this excellent article, translated it, and hope it helps other developers.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-05-19-142842.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>If you’re into redux-saga, you’ll notice plenty of libraries that help test sagas. This article compares common strategies and describes when each of five popular libraries shines.&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>Native testing (no helper library)&lt;/li>
&lt;li>&lt;a href="https://github.com/wix/redux-saga-tester" target="_blank" rel="noopener">redux-saga-tester&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/stoeffel/redux-saga-test" target="_blank" rel="noopener">redux-saga-test&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/antoinejaussoin/redux-saga-testing/" target="_blank" rel="noopener">redux-saga-testing&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jfairbank/redux-saga-test-plan" target="_blank" rel="noopener">redux-saga-test-plan&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/timbuckley/redux-saga-test-engine" target="_blank" rel="noopener">redux-saga-test-engine&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>First, a quick refresher.&lt;/p>
&lt;h2 id="what-is-a-saga">
&lt;a class="heading-anchor-link" href="#what-is-a-saga">What Is a Saga&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-a-saga"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The Redux store is a global immutable state tree. The only way to change the state is by dispatching an action, which reducers then handle. Reducers receive an action, update the state, and return a new version.&lt;/p>
&lt;p>This structure makes Redux straightforward to test, but it also means reducers are limited to state updates. How do we make an API request? In functional programming, operations that interact with the outside world are impure side effects because they’re unpredictable, time-dependent, and influenced by external data. Reducers shouldn’t contain side effects.&lt;/p>
&lt;p>Redux-Saga treats dispatched actions as signals and performs the side effects asynchronously. This keeps Redux apps nicely separated: reducers update the state, while sagas execute the side effects. We could also use redux-thunk, but over time I’ve preferred sagas—they feel more powerful and expressive.&lt;/p>
&lt;p>Middleware chains everything together. When an action is dispatched, Redux passes it through each middleware before running reducers. Redux-Saga is a middleware that runs after reducers so you can base decisions on the latest state.&lt;/p>
&lt;p>The saga middleware starts, pauses, and resumes sagas, coordinating effects from one saga to another.&lt;/p>
&lt;h2 id="effects">
&lt;a class="heading-anchor-link" href="#effects">Effects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="effects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The following saga calls an API, then dispatches either a success or failure action:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">put&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;redux-saga/effects&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Action creators
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">loadUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">username&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">payload&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">username&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">loadUserSuccess&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_SUCCESS&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">payload&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">loadUserFailure&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_FAILURE&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">payload&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Selectors
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">getContext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">state&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Reducer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">defaultState&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">reducer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">defaultState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_SUCCESS&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_FAILURE&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">default&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Saga
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">requestUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserFailure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(Every test below uses this reducer and saga.)&lt;/p>
&lt;p>&lt;code>function*&lt;/code> marks the saga as a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*" target="_blank" rel="noopener">generator&lt;/a>. When a generator yields to a called function, execution pauses until that function resumes via &lt;code>next&lt;/code> or &lt;code>throw&lt;/code>. You can pass values back into the generator so it continues until the next &lt;code>yield&lt;/code> or &lt;code>return&lt;/code>.&lt;/p>
&lt;p>In the example, the saga yields JavaScript objects produced by &lt;code>select&lt;/code>, &lt;code>call&lt;/code>, and &lt;code>put&lt;/code>. These objects are effects—declarative descriptions of work—for the saga middleware. &lt;code>select&lt;/code> reads data from state; &lt;code>call&lt;/code> invokes a function; &lt;code>put&lt;/code> dispatches an action. There are other operators, but these three are the most common.&lt;/p>
&lt;h2 id="testing-sagas">
&lt;a class="heading-anchor-link" href="#testing-sagas">Testing Sagas&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="testing-sagas"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Because effects are declarative, testing them is easier. You don’t need to call external functions. Instead, feed mock return values back into the generator and assert that the yielded objects match expectations. Effects are plain objects, so equality assertions work nicely.&lt;/p>
&lt;p>In my research I found several different approaches, from testing generators directly to using helper libraries.&lt;/p>
&lt;p>(Everything that follows is the original article’s evaluation of each library, reproduced verbatim for completeness.)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">put&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;redux-saga/effects&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Action creators
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">loadUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">username&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">payload&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">username&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">loadUserSuccess&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_SUCCESS&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">payload&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">loadUserFailure&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_FAILURE&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">payload&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Selectors
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">getContext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">state&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">context&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Reducer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">defaultState&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">reducer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">defaultState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">type&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_SUCCESS&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="s1">&amp;#39;LOAD_USER_FAILURE&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">default&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Saga
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nx">requestUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">context&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">yield&lt;/span> &lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserFailure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with native generator testing&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">isAdmin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">iterator&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">requestUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the context&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;raises the success action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;stops&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">done&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with native generator testing and errors&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Boom!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">iterator&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">requestUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the context&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;raises the failure action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserFailure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;stops after handling the error&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">done&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with redux-saga-test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">isAdmin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">saga&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">testSaga&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">requestUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the context&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;raises the success action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;stops&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">isDone&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with redux-saga-testing&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">isAdmin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">expectSaga&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">expectSagaGenerator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">requestUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the context&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">yielded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">yielded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;raises the success action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">yielded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;stops&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">done&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with redux-saga-test-plan&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">isAdmin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">actualEffects&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">testSaga&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">requestUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">inspect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">inspect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">inspect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">effect&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">isDone&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the context&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;raises the success action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;performs no further work&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;works as an integration test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSaga&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">provide&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>expectSaga&lt;/code> helper can be enhanced by reducers or other state containers, so you can run it as an integration test. After the saga finishes, assert the final state.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;works as an integration test with reducer&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">expectSaga&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSaga&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">withReducer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">reducer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">provide&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">hasFinalState&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">run&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="redux-saga-test-engine">
&lt;a class="heading-anchor-link" href="#redux-saga-test-engine">redux-saga-test-engine&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-saga-test-engine"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>redux-saga-test-engine&lt;/code> works similarly to redux-saga-test-plan. It offers &lt;code>createSagaTestEngine&lt;/code>, which takes a list of effect types generated during execution. The engine runs the saga and injects mocked return values for selectors, calls, etc.&lt;/p>
&lt;p>The resulting list of effects lets you assert the exact execution order, albeit with slightly less context.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with redux-saga-test-engine&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">isAdmin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">collectEffects&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createSagaTestEngine&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;PUT&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;CALL&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">actualEffects&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">collectEffects&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loadUserSaga&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getContext&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gets the user&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">getUser&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;raises the success action&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSuccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;performs no further work&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">actualEffects&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="redux-saga-tester">
&lt;a class="heading-anchor-link" href="#redux-saga-tester">redux-saga-tester&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux-saga-tester"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>As an integration testing framework, &lt;code>redux-saga-tester&lt;/code> provides a class that runs sagas alongside reducers, initial state, and optional middleware. Create a &lt;code>SagaTester&lt;/code>, execute the saga, and assert the final state. It also tracks effect history so you can verify order or specific subsets.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">describe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;with redux-saga-tester&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">it&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;works as an integration test with reducer&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">username&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">isAdmin&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">sagaTester&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">SagaTester&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">initialState&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">defaultState&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">reducers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">reducer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sagaTester&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">loadUserSaga&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">loadUser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;sam&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaTester&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">wasCalled&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">LOAD_USER_SUCCESS&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sagaTester&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getState&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="nx">toEqual&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loading&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">result&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">error&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">context&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test_app&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The table below outlines each testing option.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Library&lt;/th>
&lt;th style="text-align: left">Accuracy&lt;/th>
&lt;th style="text-align: left">Record Effects&lt;/th>
&lt;th style="text-align: left">Integration&lt;/th>
&lt;th style="text-align: left">Clone Generators&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Native testing&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">redux-saga-test&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">redux-saga-testing&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">redux-saga-test-plan&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">redux-saga-test-engine&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">redux-saga-tester&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;td style="text-align: left">Y&lt;/td>
&lt;td style="text-align: left">N&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Considering the current saga testing ecosystem, the recommendation is to start with the most popular option—&lt;code>redux-saga-test-plan&lt;/code>. It supports every test style, though mixing tools is perfectly valid. What matters is understanding each method’s strengths and weaknesses.&lt;/p>
&lt;p>For complete sample code, see the original article’s &lt;a href="https://blog.scottlogic.com/2018/01/16/evaluating-redux-saga-test-libraries.html" target="_blank" rel="noopener">GitHub snippets&lt;/a>.&lt;/p>
&lt;h2 id="more-to-explore">
&lt;a class="heading-anchor-link" href="#more-to-explore">More to Explore&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="more-to-explore"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://blog.scottlogic.com/shogarth/" target="_blank" rel="noopener">https://blog.scottlogic.com/shogarth/&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.scottlogic.com/category/tech.html" target="_blank" rel="noopener">https://blog.scottlogic.com/category/tech.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Many solutions exist, but following the author’s advice—using redux-saga-test-plan—keeps development and maintenance costs low. Understanding the alternatives helps deepen your saga knowledge.&lt;/li>
&lt;li>The official saga docs can be terse. For example, &lt;code>expectSaga&lt;/code> must be returned; otherwise the test appears to pass without running. Practice and keep reading. I’ll share more saga testing notes soon.&lt;/li>
&lt;/ul></description></item><item><title>[Translation] Given-When-Then in JUnit Tests</title><link>https://en.1991421.cn/2019/04/21/junitgiven-when-then/</link><pubDate>Sun, 21 Apr 2019 23:01:36 +0800</pubDate><guid>https://en.1991421.cn/2019/04/21/junitgiven-when-then/</guid><description>&lt;blockquote>
&lt;p>I am weak on testing and never formed a good testing methodology. My knowledge of common Java/JS testing frameworks is also thin, so I need a lot of learning and practice.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>I recently read a great article about testing techniques in JUnit. I translated it here to understand it better.&lt;/p>
&lt;/blockquote>
&lt;p>Original link: &lt;a href="https://blog.codecentric.de/en/2017/09/given-when-then-in-junit-tests/" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-04-21-145500.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>When we look at JUnit tests, sometimes they feel strange. We must mock objects and results, which takes a lot of work. Although we now have many advanced tools and frameworks like Mockito and PowerMock, the code written with them is sometimes not as readable, understandable, or maintainable as we would like.&lt;/p>
&lt;h2 id="structure-test-cases">
&lt;a class="heading-anchor-link" href="#structure-test-cases">Structure test cases&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="structure-test-cases"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here is a simple strategy to make JUnit tests readable without side effects.
Split tests into three parts using simple comments: &lt;code>Given, When, Then&lt;/code>. You might think a blog post for such a simple sentence is overkill, but in practice we often get lost without this structure.&lt;/p>
&lt;p>The code below is an example (simpler than real projects). But this structure helps you write better tests.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * If an item is loaded from the repository, the name of that item should
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * be transformed into uppercase.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">shouldReturnItemNameInUpperCase&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Given&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Item&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mockedItem&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Item&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;it1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Item 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;This is item 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">2000&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">when&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">itemRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">findById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;it1&amp;#34;&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="na">thenReturn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mockedItem&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// When&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">itemService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getItemNameUpperCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;it1&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Then&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">//&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">verify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">itemRepository&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">times&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="na">findById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;it1&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">assertThat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">is&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ITEM 1&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The purpose of each block is obvious. Let&amp;rsquo;s summarize.&lt;/p>
&lt;h3 id="given">
&lt;a class="heading-anchor-link" href="#given">Given&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="given"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This part sets up the mocked return values or input parameters. Mock methods are prepared here. In unit tests, this is usually the longest and most complex part.&lt;/p>
&lt;p>Note: Mockito&amp;rsquo;s &lt;code>when&lt;/code> is part of the Given phase. It is about preparing the test, so it belongs here.&lt;/p>
&lt;h3 id="when">
&lt;a class="heading-anchor-link" href="#when">When&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This is where you call the method under test. This part is usually the shortest.&lt;/p>
&lt;h3 id="then">
&lt;a class="heading-anchor-link" href="#then">Then&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="then"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This is where you assert results and verify method calls. The checks happen here.&lt;/p>
&lt;h2 id="naming-test-cases">
&lt;a class="heading-anchor-link" href="#naming-test-cases">Naming test cases&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="naming-test-cases"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Early test methods often used the &lt;code>test&lt;/code> prefix. That&amp;rsquo;s no longer common. Some people like underscores; I prefer camelCase.&lt;/p>
&lt;p>A method name should carry a lot of information, but often comments inside the test explain it better. Names like &lt;code>shouldReturnItemNameInUpperCase()&lt;/code> are clear. At the beginning of a project, it&amp;rsquo;s good to agree on a naming convention.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That is the article. A few more personal thoughts: Preparing large amounts of data for tests is painful. When multiple test cases need the same mocks, create helpers or builders to reuse them. Of course, as shared mocks grow, they can become complex and require more tests themselves. So you need to weigh tradeoffs when creating and reusing mock data.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The translation is straightforward, but as the author says, practice is where we often get lost. I will add some of my own thoughts as well.&lt;/p>
&lt;ol>
&lt;li>Underscore naming
We use underscore naming for frontend JS/TS projects and backend Java projects. The pattern is usually should_xxx_when_xxx_given_xxx. It is readable and maps directly to then/when/given in the test case.&lt;/li>
&lt;li>Use utility classes for common mock data
For complex data, we use JSON files and deserialize them for reuse. But if many test cases depend on one dataset, changing it can break many tests. It is a balancing act. Frontend and backend both use builders to provide basic data for test cases.&lt;/li>
&lt;li>Given-When-Then applies to both frontend and backend tests. It is about structuring tests, not limited to JUnit. The specific syntax may differ, but internalize the concept.&lt;/li>
&lt;/ol></description></item><item><title>A Frontend White Screen Incident</title><link>https://en.1991421.cn/2019/03/18/21c0542/</link><pubDate>Mon, 18 Mar 2019 22:47:05 +0800</pubDate><guid>https://en.1991421.cn/2019/03/18/21c0542/</guid><description>&lt;blockquote>
&lt;p>In dev the app rendered a white screen with &lt;code>You should not use &amp;lt;Link&amp;gt; outside a &amp;lt;Router&amp;gt;&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-03-18-135421.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>It looked like a React Router issue, yet the app worked locally—so the build had to be different. What exactly?&lt;/p>
&lt;h2 id="hypothesis">
&lt;a class="heading-anchor-link" href="#hypothesis">Hypothesis&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hypothesis"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Two suspects: dependency versions or build scripts. Recent commits didn’t touch the scripts, so versions felt likelier. We had edited &lt;code>package.json&lt;/code> recently without changing &lt;code>react-router&lt;/code>, so maybe CI installed a mismatched version anyway.&lt;/p>
&lt;p>Our front end builds via Maven: the plugin runs the Node build and bundles everything into a WAR.&lt;/p>
&lt;h2 id="inspect-pomxml">
&lt;a class="heading-anchor-link" href="#inspect-pomxml">Inspect POM.xml&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="inspect-pomxml"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.github.eirslett&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>frontend-maven-plugin&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>install node and npm&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>install-node-and-npm&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;nodeVersion&amp;gt;&lt;/span>${node.version}&lt;span class="nt">&amp;lt;/nodeVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;npmVersion&amp;gt;&lt;/span>${npm.version}&lt;span class="nt">&amp;lt;/npmVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;nodeDownloadRoot&amp;gt;&lt;/span>http://cdn.npm.taobao.org/dist/node/&lt;span class="nt">&amp;lt;/nodeDownloadRoot&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>npm config&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>npm&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>config set registry https://xx.cn/repository/npm/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>npm install&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>npm&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>install --registry=https://xx.cn/repository/npm/&lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>webpack build test&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>npm&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;phase&amp;gt;&lt;/span>test&lt;span class="nt">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>run webpack:test&lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;npmInheritsProxyConfigFromMaven&amp;gt;&lt;/span>false&lt;span class="nt">&amp;lt;/npmInheritsProxyConfigFromMaven&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>webpack build prod&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>npm&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;phase&amp;gt;&lt;/span>generate-resources&lt;span class="nt">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>run webpack:prod&lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;npmInheritsProxyConfigFromMaven&amp;gt;&lt;/span>false&lt;span class="nt">&amp;lt;/npmInheritsProxyConfigFromMaven&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>CI used &lt;code>npm install&lt;/code>. Locally we run Yarn, which respects &lt;code>yarn.lock&lt;/code>. Without the lock file, npm resolved slightly different versions—hence the runtime error.&lt;/p>
&lt;h2 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Switch the Maven build to Yarn:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.github.eirslett&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>frontend-maven-plugin&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>install node and yarn&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>install-node-and-yarn&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;nodeVersion&amp;gt;&lt;/span>${node.version}&lt;span class="nt">&amp;lt;/nodeVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;yarnVersion&amp;gt;&lt;/span>${yarn.version}&lt;span class="nt">&amp;lt;/yarnVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>yarn config&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>yarn&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>config set registry https://xx.cn/repository/npm/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>yarn install&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>yarn&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>webpack build test&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>yarn&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;phase&amp;gt;&lt;/span>test&lt;span class="nt">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>run webpack:test&lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;yarnInheritsProxyConfigFromMaven&amp;gt;&lt;/span>false&lt;span class="nt">&amp;lt;/yarnInheritsProxyConfigFromMaven&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>webpack build prod&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;goal&amp;gt;&lt;/span>yarn&lt;span class="nt">&amp;lt;/goal&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/goals&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;phase&amp;gt;&lt;/span>generate-resources&lt;span class="nt">&amp;lt;/phase&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;arguments&amp;gt;&lt;/span>run webpack:prod&lt;span class="nt">&amp;lt;/arguments&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;yarnInheritsProxyConfigFromMaven&amp;gt;&lt;/span>false&lt;span class="nt">&amp;lt;/yarnInheritsProxyConfigFromMaven&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/execution&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/executions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Also update &lt;code>package.json&lt;/code> scripts to run Yarn (&lt;code>yarn run build&lt;/code>, etc.). After both changes, the builds aligned and the white screen disappeared. Root cause: mismatch between dev and CI dependency versions.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="what-lock-files-do">
&lt;a class="heading-anchor-link" href="#what-lock-files-do">What lock files do&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-lock-files-do"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>lock&lt;/code> literally means lock. &lt;code>package-lock.json&lt;/code> (npm) and &lt;code>yarn.lock&lt;/code> (Yarn) freeze exact versions. Even if &lt;code>package.json&lt;/code> has version ranges, lock files make installs deterministic.&lt;/p>
&lt;p>From the Yarn docs:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">yarn&lt;/span> &lt;span class="n">install&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Install&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">dependencies&lt;/span> &lt;span class="n">listed&lt;/span> &lt;span class="n">within&lt;/span> &lt;span class="n">package&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">local&lt;/span> &lt;span class="n">node_modules&lt;/span> &lt;span class="n">folder&lt;/span>&lt;span class="o">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">The&lt;/span> &lt;span class="n">yarn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lock&lt;/span> &lt;span class="n">file&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">utilized&lt;/span> &lt;span class="n">as&lt;/span> &lt;span class="n">follows&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">If&lt;/span> &lt;span class="n">yarn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lock&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">present&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">enough&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">satisfy&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">dependencies&lt;/span> &lt;span class="n">listed&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">package&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">exact&lt;/span> &lt;span class="n">versions&lt;/span> &lt;span class="n">recorded&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">yarn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lock&lt;/span> &lt;span class="n">are&lt;/span> &lt;span class="n">installed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">yarn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lock&lt;/span> &lt;span class="n">will&lt;/span> &lt;span class="n">be&lt;/span> &lt;span class="n">unchanged&lt;/span>&lt;span class="o">.&lt;/span> &lt;span class="n">Yarn&lt;/span> &lt;span class="n">will&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">check&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">newer&lt;/span> &lt;span class="n">versions&lt;/span>&lt;span class="o">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">If&lt;/span> &lt;span class="n">yarn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lock&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">absent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">enough&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">satisfy&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">dependencies&lt;/span> &lt;span class="n">listed&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">package&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="n">example&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">you&lt;/span> &lt;span class="n">manually&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">dependency&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">package&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">Yarn&lt;/span> &lt;span class="n">looks&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">newest&lt;/span> &lt;span class="n">versions&lt;/span> &lt;span class="n">available&lt;/span> &lt;span class="n">that&lt;/span> &lt;span class="n">satisfy&lt;/span> &lt;span class="n">the&lt;/span> &lt;span class="n">constraints&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">package&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span> &lt;span class="n">The&lt;/span> &lt;span class="n">results&lt;/span> &lt;span class="n">are&lt;/span> &lt;span class="n">written&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">yarn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="o">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="yarn-vs-npm">
&lt;a class="heading-anchor-link" href="#yarn-vs-npm">Yarn vs. npm&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="yarn-vs-npm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Both are package managers. Yarn arrived later, tends to be faster, and its CLI feels more semantic. Choose whichever your team standardizes on.&lt;/p>
&lt;h3 id="frontend-maven-plugin">
&lt;a class="heading-anchor-link" href="#frontend-maven-plugin">frontend-maven-plugin?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-maven-plugin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Maven is pervasive in Java projects. &lt;code>frontend-maven-plugin&lt;/code> lets us bundle a Node environment with the build, keeping it isolated from the system installation and making deployments portable. See the plugin docs for more usage details.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Every incident is a learning opportunity—this one reinforced why consistent dependency management matters.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-03-18-143213.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/eirslett/frontend-maven-plugin" target="_blank" rel="noopener">https://github.com/eirslett/frontend-maven-plugin&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.npmjs.com/cli/install" target="_blank" rel="noopener">https://docs.npmjs.com/cli/install&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://yarnpkg.com/en/docs/cli/install" target="_blank" rel="noopener">https://yarnpkg.com/en/docs/cli/install&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>dependencies vs devDependencies</title><link>https://en.1991421.cn/2019/02/17/dependencies-vs-devdependencies/</link><pubDate>Sun, 17 Feb 2019 17:22:49 +0800</pubDate><guid>https://en.1991421.cn/2019/02/17/dependencies-vs-devdependencies/</guid><description>&lt;blockquote>
&lt;p>npm is the package manager for the Node.js platform. In actual development, we don&amp;rsquo;t start from scratch but often need to install numerous packages for development and production use. At this point, we need to install packages and configure them in the package.json file to clarify their dependencies. In the package.json file, there are two dependency declaration locations: dependencies and devDependencies. So for a given package, should we install it in dependencies or devDependencies? This article aims to clarify this difference.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-02-17-092336.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="official-definitions">
&lt;a class="heading-anchor-link" href="#official-definitions">Official Definitions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="official-definitions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s start with authoritative official overviews&lt;/p>
&lt;h3 id="dependencies">
&lt;a class="heading-anchor-link" href="#dependencies">dependencies&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dependencies"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Dependencies are specified in a simple object that maps a package name to a version range. The version range is a string which has one or more space-separated descriptors. Dependencies can also be identified with a tarball or git URL.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Please do not put test harnesses or transpilers in your dependencies object. See devDependencies, below.&lt;/p>
&lt;/blockquote>
&lt;p>The official documentation clearly states that &lt;code>test harnesses and transpilers should not be placed in dependencies, but rather in devDependencies&lt;/code>.&lt;/p>
&lt;h3 id="devdependencies">
&lt;a class="heading-anchor-link" href="#devdependencies">devDependencies&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="devdependencies"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>If someone is planning on downloading and using your module in their program, then they probably don’t want or need to download and build the external test or documentation framework that you use.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>In this case, it’s best to map these additional items in a devDependencies object.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>These things will be installed when doing npm link or npm install from the root of a package, and can be managed like any other npm configuration param. See npm-config for more on the topic.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>For build steps that are not platform-specific, such as compiling CoffeeScript or other languages to JavaScript, use the prepare script to do this, and make the required package a devDependency.
To summarize briefly:
&lt;code>Build requirements that are not platform-specific, such as compiling CoffeeScript or other languages to JavaScript, should be set as devDependencies.&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;h2 id="practical-example">
&lt;a class="heading-anchor-link" href="#practical-example">Practical Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="practical-example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s an actual project I worked on - a React frontend project that was in the process of migrating from JS to TS, so it included Babel.&lt;/p>
&lt;p>Here&amp;rsquo;s the general package dependency table, and we&amp;rsquo;ll use some of these packages to illustrate the differences.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;dependencies&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@fortawesome/fontawesome-svg-core&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@fortawesome/free-solid-svg-icons&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.3.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@fortawesome/react-fontawesome&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.1.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;availity-reactstrap-validation&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.0.6&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;axios&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.18.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-polyfill&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^6.26.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bootstrap&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.1.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;classnames&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.2.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;deep-equal&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;latest&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;file-saver&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;history&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.7.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;html2canvas&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.0.0-alpha.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;immutable&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.8.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jest-enzyme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^7.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;loaders.css&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.1.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;lodash&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.17.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;moment&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.22.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;normalize.css&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^7.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;promise&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;8.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;prop-types&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^15.6.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;query-string&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-dropdown&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-input-number&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.0.6&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-menu&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^7.4.20&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-pagination&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^1.14.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-select&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^7.5.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-slider&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^8.6.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rc-tooltip&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.7.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;16.4.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-dom&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;16.4.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-hot-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-intl&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.7.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-modal&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.3.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-redux&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.0.7&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-redux-loading-bar&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.0.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-router-dom&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.3.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-select&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-sizeme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.5.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-toastify&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-transition-group&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.5.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-actions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.3.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-actions-helper&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^1.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-devtools&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.4.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-devtools-dock-monitor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.1.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-devtools-log-monitor&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.4.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-promise-middleware&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-saga&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^0.16.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-thunk&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.3.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;reselect&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source-map-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^0.2.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;superagent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.6.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jspdf&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.5.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;xml2js&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.4.19&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;devDependencies&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/enzyme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.1.13&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;23.3.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/lodash&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.14.119&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/node&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;10.9.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;16.4.12&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/react-dom&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;16.0.7&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/react-intl&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.3.15&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/react-redux&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6.0.6&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/react-router-dom&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.3.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/redux&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.6.31&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;@types/webpack-env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.13.6&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;autoprefixer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;9.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-core&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6.25.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-eslint&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.2.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;20.0.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-plugin-transform-decorators-legacy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^1.3.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-polyfill&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^6.26.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-preset-es2015&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^6.24.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-preset-es2016&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^6.24.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-preset-react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^6.24.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-preset-react-app&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.0.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-preset-stage-0&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^6.24.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;babel-runtime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;6.26.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;browser-sync&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.26.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;browser-sync-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.2.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;cache-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;codelyzer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.1.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;copy-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.5.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;cross-env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;css-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enzyme&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enzyme-adapter-react-16&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.3.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enzyme-to-json&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.3.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.9.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint-config-react-app&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^2.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.9.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint-plugin-flowtype&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.35.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint-plugin-import&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint-plugin-jsx-a11y&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;eslint-plugin-react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;exports-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.6.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;express&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.12.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;extract-text-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;file-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;fork-ts-checker-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.4.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;friendly-errors-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;html-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;html-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;husky&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;identity-obj-proxy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jasmine-core&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;23.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jest-junit&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;jest-sonar-reporter&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;json-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.5.7&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.7.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-chrome-launcher&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-coverage&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-intl-shim&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.0.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-jasmine&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-junit-reporter&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-notify-reporter&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-remap-istanbul&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.6.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-sourcemap-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.3.7&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;karma-webpack&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.0.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;less&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.0.0-alpha.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;less-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.0.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;lint-staged&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7.3.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-dev-utils&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^4.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;react-infinite-scroller&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-mock-store&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.5.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;redux-saga-test-plan&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^3.7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rimraf&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.6.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;simple-progress-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.1.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source-map-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.2.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;string-replace-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.1.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;style-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.22.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;swagger&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^0.7.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;swagger-express-mw&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^0.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;swagger-ui&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2.2.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;terser-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.0.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;thread-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;to-string-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.1.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ts-jest&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;23.1.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ts-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.5.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tslint&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.11.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tslint-config-prettier&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.15.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tslint-eslint-rules&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5.4.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tslint-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.6.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tslint-react&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.6.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;typescript&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;uglifyjs-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^1.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;url-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;^0.6.2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;web-app-manifest-loader&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0.1.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;webpack&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.17.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;webpack-cli&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.1.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;webpack-dev-server&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.1.6&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;webpack-merge&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;4.1.4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;webpack-notifier&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.7.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;workbox-webpack-plugin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;3.4.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>React and related component libraries or redux libraries, rc-dropdown, redux - these are actually needed by the platform, so they go in dependencies&lt;/li>
&lt;li>For example, @types packages are only needed during development phase. After actual compilation, it&amp;rsquo;s just JS, so they&amp;rsquo;re not essential and go in devDependencies&lt;/li>
&lt;li>For example, babel, webpack related packages, karma, and other transpilers, build tools, test frameworks, etc. - according to official documentation, these should all go in devDependencies&lt;/li>
&lt;/ul>
&lt;h3 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Some developers don&amp;rsquo;t seem to care about the difference between dependencies and devDependencies, placing them randomly without affecting development or deployment. However, this approach is not ideal.&lt;/p>
&lt;p>When we properly maintain the dependency relationships in the package.json file, we gain two benefits:&lt;/p>
&lt;ol>
&lt;li>During production deployment, when we execute &lt;code>npm install --production&lt;/code> or set the environment variable &lt;code>NODE_ENV&lt;/code> to &lt;code>production&lt;/code>, none of the packages under devDependencies will be installed. &lt;code>This makes the overall package size much smaller, with fewer packages, and naturally faster deployment speed&lt;/code>.&lt;/li>
&lt;li>Through the file, we clearly understand development vs production dependencies, reducing maintenance costs&lt;/li>
&lt;/ol></description></item><item><title>Improve Frontend Code Quality with Tooling</title><link>https://en.1991421.cn/2019/01/01/improve-frontend-code-quality-tools/</link><pubDate>Tue, 01 Jan 2019 20:38:08 +0800</pubDate><guid>https://en.1991421.cn/2019/01/01/improve-frontend-code-quality-tools/</guid><description>&lt;blockquote>
&lt;p>“Messy code makes teammates cry.” Our team felt that pain recently. Refactoring was part of the answer, but we also wanted guardrails that detect issues automatically. This post covers the tools we adopted and how they fit together.&lt;/p>
&lt;/blockquote>
&lt;p>We rely on the following toolbox:
&lt;code>EditorConfig&lt;/code>, &lt;code>TSLint/ESLint&lt;/code>, &lt;code>TypeScript (tsc)&lt;/code>, &lt;code>Prettier&lt;/code>, &lt;code>Husky&lt;/code>, and &lt;code>lint-staged&lt;/code>.&lt;/p>
&lt;h2 id="editorconfig">
&lt;a class="heading-anchor-link" href="#editorconfig">EditorConfig&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="editorconfig"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Keeps indentation, line endings, and whitespace consistent across editors/OSes. My baseline config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">root&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[*]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">indent_style&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">space&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">indent_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">end_of_line&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">lf&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">charset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">utf-8&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">trim_trailing_whitespace&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">insert_final_newline&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[*.md]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">trim_trailing_whitespace&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[*.{css,scss,less,js,jsx,json,ts,tsx,sass,php,html,hbs,mustache,phtml,html.twig}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">indent_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">2&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Install the EditorConfig plugin in your IDE and drop &lt;code>.editorconfig&lt;/code> in the project root.&lt;/p>
&lt;h2 id="linting-tslint--eslint">
&lt;a class="heading-anchor-link" href="#linting-tslint--eslint">Linting (TSLint → ESLint)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="linting-tslint--eslint"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We were midway through a JS → TS migration, so we kept TSLint at the time; ESLint is the modern choice. Lint rules flag style/readability issues in the editor and during builds.&lt;/p>
&lt;p>Key points:&lt;/p>
&lt;ul>
&lt;li>&lt;code>rules&lt;/code> apply to &lt;code>.ts/.tsx&lt;/code>; &lt;code>jsRules&lt;/code> can mirror them for &lt;code>.js/.jsx&lt;/code> during transitions.&lt;/li>
&lt;li>Most IDEs support “Apply TSLint Code Style Rules” for quick fixes.&lt;/li>
&lt;/ul>
&lt;h2 id="typescript-compiler-tsc">
&lt;a class="heading-anchor-link" href="#typescript-compiler-tsc">TypeScript Compiler (&lt;code>tsc&lt;/code>)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript-compiler-tsc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Linters don’t catch type errors. Running &lt;code>tsc --noEmit&lt;/code> in CI and git hooks surfaces them early. The screenshot below shows an error that TSLint missed but &lt;code>tsc&lt;/code> found:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-10-143321.png"
alt="tsc error"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="prettier">
&lt;a class="heading-anchor-link" href="#prettier">Prettier&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prettier"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Prettier handles formatting; lint handles correctness. Example config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">printWidth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">140&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">singleQuote&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">tabWidth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">useTabs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">arrowParens&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">avoid&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jsxBracketSameLine&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Install the Prettier IDE plugin, add &lt;code>prettier&lt;/code> as a dev dependency, and create &lt;code>.prettierrc.yaml&lt;/code>.&lt;/p>
&lt;h2 id="husky--lint-staged">
&lt;a class="heading-anchor-link" href="#husky--lint-staged">Husky + lint-staged&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="husky--lint-staged"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Husky runs scripts during Git hooks. We run &lt;code>tsc&lt;/code> and &lt;code>lint-staged&lt;/code> on &lt;code>pre-commit&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;hooks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pre-commit&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;tsc --noEmit &amp;amp;&amp;amp; lint-staged&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>lint-staged&lt;/code> limits Prettier/TSLint to staged files so commits only touch relevant lines:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;lint-staged&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;{,src/**/}*.{ts,tsx,js,jsx}&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tslint --fix&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;prettier --write&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;{,src/**/}*.{md,json,css,scss}&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;prettier --write&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="ide-formatting-quirks">
&lt;a class="heading-anchor-link" href="#ide-formatting-quirks">IDE Formatting Quirks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ide-formatting-quirks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Our default “format” shortcut didn’t respect Prettier/TSLint rules (e.g., self-closing JSX tags lost the space). Solution: adjust IDE formatting settings or map a dedicated Prettier hotkey so formatting is consistent.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-08-21-133557.jpg"
alt="IDE settings"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="takeaways">
&lt;a class="heading-anchor-link" href="#takeaways">Takeaways&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="takeaways"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>EditorConfig → consistent edits&lt;/li>
&lt;li>Lint + TypeScript → enforce conventions and catch bugs&lt;/li>
&lt;li>Prettier → shared formatting baseline&lt;/li>
&lt;li>Husky + lint-staged → ensure checks run before code lands in &lt;code>main&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Investing once in this toolchain pays off every day. It keeps the repository clean so we can focus on building features.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.jianshu.com/p/cdd749c624d9" target="_blank" rel="noopener">Using ESLint and lint-staged to improve code quality&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/entry/5b90f94a5188255c304fce78" target="_blank" rel="noopener">Thoughts on maintaining frontend quality&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Git Merge vs Rebase: Differences and Choices</title><link>https://en.1991421.cn/2018/12/09/gitmergerebase/</link><pubDate>Sun, 09 Dec 2018 20:54:22 +0800</pubDate><guid>https://en.1991421.cn/2018/12/09/gitmergerebase/</guid><description>&lt;blockquote>
&lt;p>Previously, I always used merge when pulling code, and when encountering conflicts, I would resolve them directly. This approach worked fine. But recently, I joined a project where the team size and code submission frequency are both high, leading to frequent conflicts when pulling code. Using merge results in multiple lines in the log graph with back-and-forth merging situations, which becomes very messy.&lt;/p>
&lt;p>To address this situation, the team advocates using rebase to pull code. But what are the advantages? I&amp;rsquo;ve researched some materials and organized them here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="creating-branches">
&lt;a class="heading-anchor-link" href="#creating-branches">Creating Branches&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="creating-branches"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Git&amp;rsquo;s branching capability allows us to develop many features in parallel without interfering with each other. After completion, we use MR (Merge Request) to merge into the main branch, such as dev or master.&lt;/p>
&lt;h2 id="merging-branches">
&lt;a class="heading-anchor-link" href="#merging-branches">Merging Branches&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="merging-branches"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We develop using the branching model and then want to incorporate the developed feature code into the main branch. This method is called merging, which in GitLab is called MR.&lt;/p>
&lt;p>To elaborate: Git is distributed, so for example, the upstream server we submit to has multiple branches, and each of our development machines also has multiple branches. Branch merging doesn&amp;rsquo;t just refer to merging A and B on origin; actually, when we submit our local branch to the corresponding upstream branch, it&amp;rsquo;s also a branch merge.&lt;/p>
&lt;h2 id="handling-branch-conflicts">
&lt;a class="heading-anchor-link" href="#handling-branch-conflicts">Handling Branch Conflicts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-branch-conflicts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Conflicts arise when code modifications occur in the same location. At this point, Git doesn&amp;rsquo;t know which version to choose, so it requires you to operate, such as accepting theirs, yours, or manually combining both.&lt;/p>
&lt;h3 id="handling-conflicts-with-merge">
&lt;a class="heading-anchor-link" href="#handling-conflicts-with-merge">Handling Conflicts with Merge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-conflicts-with-merge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When merging branches using the merge method, it doesn&amp;rsquo;t preserve the commits from the merged branch. For example, in the diagram below, brown represents sprint27 and purple represents master. When merged, it only counts as one merge commit.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-09-043723.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="handling-conflicts-with-rebase">
&lt;a class="heading-anchor-link" href="#handling-conflicts-with-rebase">Handling Conflicts with Rebase&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-conflicts-with-rebase"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When merging branches using the rebase method, it incorporates all commits from the merged branch into the target branch line and doesn&amp;rsquo;t create new commits.&lt;/p>
&lt;p>Assuming before merging it looks like this:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-09-122640.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After merge:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-09-122651.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After rebase:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-09-122730.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After getting used to frequently pulling code with rebase, the project&amp;rsquo;s timeline now basically becomes a straight line. It&amp;rsquo;s simple and clear who submitted what each time.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-09-123124.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>The more I work with Git, the more I find it simple, powerful, and flexible. My understanding of it continues to strengthen through practice. Someone at our company once said that making something complex appear simple to use is difficult, and I think Git is exactly that.&lt;/li>
&lt;li>Regarding rebase vs merge operations, after discussing with several friends and considering team situations, I believe the following &lt;code>best practices&lt;/code> are optimal:
&lt;ul>
&lt;li>When fetching remote project code, use &lt;code>git pull --rebase&lt;/code>&lt;/li>
&lt;li>When merging branches, use &lt;code>git merge --no-ff&lt;/code> to create a merge commit&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://ihower.tw/blog/archives/3843" target="_blank" rel="noopener">Using git rebase to avoid unnecessary merge&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.cnblogs.com/xueweihan/p/5743327.html" target="_blank" rel="noopener">[git] Differences between merge and rebase&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Use Redux in React (Step-by-Step Guide)</title><link>https://en.1991421.cn/2018/12/04/reactredux/</link><pubDate>Tue, 04 Dec 2018 23:44:03 +0800</pubDate><guid>https://en.1991421.cn/2018/12/04/reactredux/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-02-090726.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>Recently I worked on Project A with a React + Redux stack. My React foundation was weak and Redux was new, so I ran into many issues. After reading docs and practice, I now have a basic understanding and record it here.&lt;/p>
&lt;/blockquote>
&lt;p>For Redux, those who know it say it&amp;rsquo;s simple, those who don&amp;rsquo;t say it&amp;rsquo;s hard. Is it really hard? Not really. Let me explain.&lt;/p>
&lt;p>Before talking about Redux in React, let&amp;rsquo;s address a few questions. Understanding them helps a lot.&lt;/p>
&lt;h2 id="why-do-spas-exist">
&lt;a class="heading-anchor-link" href="#why-do-spas-exist">Why do SPAs exist?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-do-spas-exist"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Redux was created to solve state management in SPAs, so we should understand why SPAs exist. When we used jQuery, we didn&amp;rsquo;t need Redux. We should not use Redux or React just because others do. If it&amp;rsquo;s unnecessary, why add complexity?&lt;/p>
&lt;p>Recommended reading: &lt;a href="https://medium.com/dailyjs/the-deepest-reason-why-modern-javascript-frameworks-exist-933b86ebc445" target="_blank" rel="noopener">The Fundamental Reason Why Modern JS Frameworks Exist&lt;/a>.&lt;/p>
&lt;p>Modern frontend frameworks mainly solve UI and state synchronization.&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>Modern JS frameworks mainly solve the problem of UI and state synchronization.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>Using only native JS makes it difficult to write complex, efficient, and maintainable UI code.&lt;/li>
&lt;li>Web components don&amp;rsquo;t solve this main problem.&lt;/li>
&lt;li>Although it&amp;rsquo;s easy to create a framework that solves this problem using virtual DOM libraries, it&amp;rsquo;s not recommended that you actually do this.&lt;/li>
&lt;/ul>
&lt;h2 id="what-problems-does-redux-solve-for-spas">
&lt;a class="heading-anchor-link" href="#what-problems-does-redux-solve-for-spas">What problems does Redux solve for SPAs?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-problems-does-redux-solve-for-spas"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>SPAs make frontend development more systematic but bring state management problems. Redux emerged to address this.
The Redux official site explains it well:&lt;/p>
&lt;blockquote>
&lt;p>As the requirements for JavaScript single-page applications have become increasingly complicated, our code must manage more state than ever before. This state can include server responses and cached data, as well as locally created data that has not yet been persisted to the server. UI state is also increasing in complexity, as we need to manage active routes, selected tabs, spinners, pagination controls, and so on.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Managing this ever-changing state is hard. If a model can update another model, then a view can update a model, which updates another model, and this, in turn, might cause another view to update. At some point, you no longer understand what happens in your app as you have lost control over the when, why, and how of its state. When a system is opaque and non-deterministic, it&amp;rsquo;s hard to reproduce bugs or add new features.&lt;/p>
&lt;/blockquote>
&lt;h2 id="react-vs-redux">
&lt;a class="heading-anchor-link" href="#react-vs-redux">React vs Redux&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-vs-redux"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Concepts (from official docs):&lt;/p>
&lt;h3 id="react">
&lt;a class="heading-anchor-link" href="#react">React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>A JavaScript library for building user interfaces&lt;/p>
&lt;h3 id="redux">
&lt;a class="heading-anchor-link" href="#redux">Redux&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="redux"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Redux is a predictable state container for JavaScript apps&lt;/p>
&lt;h3 id="react-redux">
&lt;a class="heading-anchor-link" href="#react-redux">react-redux&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react-redux"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>From the above, they are unrelated libraries solving different layers. In development we often face both problems, so React projects use Redux for complex state management. The glue is react-redux, which you can think of as the React version of Redux.&lt;/p>
&lt;h2 id="using-redux-in-react">
&lt;a class="heading-anchor-link" href="#using-redux-in-react">Using Redux in React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-redux-in-react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now that we understand the big picture, let&amp;rsquo;s learn the basics.&lt;/p>
&lt;p>For basic Redux learning, I recommend the official site and revisiting it often.&lt;/p>
&lt;h3 id="install-redux">
&lt;a class="heading-anchor-link" href="#install-redux">Install Redux&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-redux"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm i redux --save-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm i react-redux --save-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="create-action-reducer-store">
&lt;a class="heading-anchor-link" href="#create-action-reducer-store">Create Action, Reducer, Store&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-action-reducer-store"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="action">Action&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-04-153755.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="reducer">Reducer&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-04-153837.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="store">Store&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-04-153911.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="dispatch">dispatch&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-04-154143.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>See code &lt;a href="https://github.com/reduxjs/redux/tree/master/examples/todos" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;h4 id="relationship-between-the-three">Relationship between the three&lt;/h4>&lt;p>If you don&amp;rsquo;t understand the three pillars, see the diagram below:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-12-02-134719.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Let&amp;rsquo;s break it down:&lt;/p>
&lt;ul>
&lt;li>Components trigger actions via store.dispatch()&lt;/li>
&lt;li>Actions kick off operations, reducers compute the new state&lt;/li>
&lt;li>Store belongs to Redux. In React, a component has state and props. Redux store is reflected into component props for consumption. The connect function connects the store to the component.&lt;/li>
&lt;/ul>
&lt;h3 id="using-helper-functions">
&lt;a class="heading-anchor-link" href="#using-helper-functions">Using helper functions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-helper-functions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In addition to redux itself, it&amp;rsquo;s recommended to use helper libraries to standardize and improve the experience.&lt;/p>
&lt;h4 id="redux-actions-helper">redux-actions-helper&lt;/h4>&lt;p>I used this in my project to help with actions.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">initSettingAction&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createAction&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">SettingActionTypes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">UPDATE_SETTING&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">inherit&lt;/span>: &lt;span class="kt">ISetting&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">base&lt;/span>: &lt;span class="kt">ISetting&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inherit&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">base&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">updateSettingReducer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">state&lt;/span>: &lt;span class="kt">ISettingState&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">action&lt;/span>: &lt;span class="kt">IAction&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="p">{&lt;/span> &lt;span class="nx">inherit&lt;/span>: &lt;span class="kt">ISetting&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">base&lt;/span>: &lt;span class="kt">ISetting&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>&lt;span class="nx">action&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">payload&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Benefits:&lt;/p>
&lt;ol>
&lt;li>It enforces that action parameters are separated from type, matching Flux: payload and type are siblings, all params live under payload.&lt;/li>
&lt;li>IAction type definition enforces type safety for actions.&lt;/li>
&lt;/ol>
&lt;h4 id="redux-toolkit">Redux Toolkit&lt;/h4>&lt;p>The official team released a toolkit; new projects can consider it.&lt;/p>
&lt;p>&lt;a href="https://github.com/reduxjs/redux-toolkit" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You should now have a basic understanding. This post is mostly fundamentals. Internal skills matter most. Once you know the why, the rest is practice.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://taobaofed.org/blog/2016/08/18/react-redux-connect/" target="_blank" rel="noopener">React practice: react-redux connect explained&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>[Translation] Core JavaScript Concepts</title><link>https://en.1991421.cn/2018/11/11/javascript/</link><pubDate>Sun, 11 Nov 2018 23:24:19 +0800</pubDate><guid>https://en.1991421.cn/2018/11/11/javascript/</guid><description>&lt;p>&lt;a href="https://medium.freecodecamp.org/learn-these-core-javascript-concepts-in-just-a-few-minutes-f7a16f42c1b0" target="_blank" rel="noopener">Original article link&lt;/a>&lt;/p>
&lt;ol>
&lt;li>Scope&lt;/li>
&lt;li>IIFE (Immediately Invoked Function Expression)&lt;/li>
&lt;li>MVC&lt;/li>
&lt;li>Async/Await&lt;/li>
&lt;li>Closure&lt;/li>
&lt;li>Callback&lt;/li>
&lt;/ol>
&lt;h2 id="scope">
&lt;a class="heading-anchor-link" href="#scope">Scope&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scope"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Scope is a box with boundaries. There are two types of boundaries in JS: local and global, also known as internal and external.&lt;/p>
&lt;p>Local means you have permission to access within this block, while global means accessible everywhere.&lt;/p>
&lt;p>When we talk about classes, functions, and methods, they all involve this. We use scope to determine what is accessible (visibility) in the current context.&lt;/p>
&lt;h3 id="key-points">
&lt;a class="heading-anchor-link" href="#key-points">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Logical separation&lt;/li>
&lt;li>Reduced focus&lt;/li>
&lt;li>Improved readability&lt;/li>
&lt;/ul>
&lt;h3 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Suppose we create a function and want to access variables in the global scope&lt;/p>
&lt;ul>
&lt;li>
&lt;p>ES5&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-05-143736.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ES6&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-05-143851.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>As shown in the example above, the function showName() has access to both local and global variables. But note that the global scope does not have permission to access locally defined variables unless there&amp;rsquo;s a return value.&lt;/p>
&lt;h2 id="iife-immediately-invoked-function-expression">
&lt;a class="heading-anchor-link" href="#iife-immediately-invoked-function-expression">IIFE (Immediately Invoked Function Expression)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="iife-immediately-invoked-function-expression"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>IIFE (Immediately Invoked Function Expression), as its name suggests, is triggered immediately upon creation. Before ES6, people typically wrapped IIFE into a class name and then triggered it in a return statement.&lt;/p>
&lt;h3 id="key-points-1">
&lt;a class="heading-anchor-link" href="#key-points-1">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Executes immediately&lt;/li>
&lt;li>Avoids global scope pollution&lt;/li>
&lt;li>Supports asynchronous structures&lt;/li>
&lt;li>Improves readability (some voices object)&lt;/li>
&lt;/ul>
&lt;h3 id="example-1">
&lt;a class="heading-anchor-link" href="#example-1">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="es5">ES5&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-081606.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="es6">
&lt;a class="heading-anchor-link" href="#es6">ES6&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="es6"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-081654.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="mvc">
&lt;a class="heading-anchor-link" href="#mvc">MVC&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mvc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Model-view-controller is a design pattern, not a programming language. Nowadays, almost 85% of web applications follow this pattern. There are other design patterns, but this one is more fundamental and easier to understand.&lt;/p>
&lt;h3 id="key-points-2">
&lt;a class="heading-anchor-link" href="#key-points-2">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Scalability and maintainability&lt;/li>
&lt;li>Easy to improve, update, and debug&lt;/li>
&lt;li>Simple to get started&lt;/li>
&lt;li>Provides structure and overview&lt;/li>
&lt;/ul>
&lt;h3 id="example-2">
&lt;a class="heading-anchor-link" href="#example-2">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="es5-1">ES5&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-082119.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="es6-1">ES6&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-082131.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>It should be noted that the usual best practice is to split View, Model, and Controller into different folders and files. For better illustration, they are not split here. The purpose of design patterns is to simplify the development process and build a collaborative development environment that is easy to maintain.&lt;/p>
&lt;h2 id="asyncawait">
&lt;a class="heading-anchor-link" href="#asyncawait">Async/await&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="asyncawait"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;strong>Stop until a certain action is completed.&lt;/strong> This technique provides a way to maintain asynchronous processing. For example, before allowing a user to log into the system, you need to check if the user&amp;rsquo;s password is correct (compare with information stored on the server). Or you&amp;rsquo;ve provided a REST API request, and before entering the view, you want to wait for the data to load completely.&lt;/p>
&lt;h3 id="key-points-3">
&lt;a class="heading-anchor-link" href="#key-points-3">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points-3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Asynchronous capability&lt;/li>
&lt;li>Behavior control&lt;/li>
&lt;li>Reduces &amp;ldquo;callback hell&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h3 id="example-3">
&lt;a class="heading-anchor-link" href="#example-3">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-3"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Suppose we request a REST API to get all users and display the results in JSON format&lt;/p>
&lt;h4 id="es5-2">ES5&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-083413.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="es6-2">ES6&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-083455.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>To use await, we must include it in an async function, thereby telling JS that we are using promises. In the above example, we are waiting for two results: response and users. Before converting response to JSON format, we need to make sure we have the response, otherwise we cannot convert it, which would be an error.&lt;/p>
&lt;h2 id="closures">
&lt;a class="heading-anchor-link" href="#closures">Closures&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="closures"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;strong>Simply put, closures are functions within functions.&lt;/strong> Closures are useful when you want to pass variables, methods, or arrays from an outer function to an inner function. Or when an inner function can access the context of an outer function (but not vice versa), closures are also useful.&lt;/p>
&lt;h3 id="key-points-4">
&lt;a class="heading-anchor-link" href="#key-points-4">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points-4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Extends behavior&lt;/li>
&lt;li>Useful when handling events&lt;/li>
&lt;/ul>
&lt;h3 id="example-4">
&lt;a class="heading-anchor-link" href="#example-4">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Suppose we are now a development engineer at Volvo and need a function that can print the car name.&lt;/p>
&lt;h4 id="es5-3">ES5&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-083932.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="es6-3">ES6&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-083946.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>showName is a closure because it extends the behavior of the showInfo function and also has permission to access the carType variable.&lt;/p>
&lt;h2 id="callbacks">
&lt;a class="heading-anchor-link" href="#callbacks">Callbacks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="callbacks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A callback function is a function that executes after another function completes. In the JS world, if a function is waiting for another function to execute or return a value, then that function is called a callback function. Callback functions are a way to handle asynchronous operations.&lt;/p>
&lt;h3 id="key-points-5">
&lt;a class="heading-anchor-link" href="#key-points-5">Key points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-points-5"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Waiting for event execution&lt;/li>
&lt;li>Provides asynchronous capability&lt;/li>
&lt;li>A means of handling chained functions&lt;/li>
&lt;li>You may have heard of callback hell, which means you&amp;rsquo;ll see an iterative callback structure (a callback within a callback, and so on)&lt;/li>
&lt;/ul>
&lt;h3 id="example-5">
&lt;a class="heading-anchor-link" href="#example-5">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-5"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Suppose Musk needs a feature that triggers a rocket launch when a button is clicked&lt;/p>
&lt;h4 id="es5-4">ES5&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-145155.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="es6-4">ES6&lt;/h4>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-11-145213.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note that we&amp;rsquo;re waiting for an event to occur before triggering an action to execute. We pass the &lt;code>fireUpEngines()&lt;/code> function as a parameter to the &lt;code>pressButton()&lt;/code> function, and when the user clicks the button, it triggers the rocket launch.&lt;/p>
&lt;p>That&amp;rsquo;s it. Some basic concepts of JS are explained here through examples.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This article is very basic, but very key, so take it seriously. Any language, technology, or discipline develops from basic principles and concepts. The JS standard ES has expanded a lot from version 1 to versions 7 and 8 now. But basic concepts don&amp;rsquo;t develop and expand that quickly. For example, scope, closures, asynchrony, and cross-domain - it was like this 10 years ago, and isn&amp;rsquo;t it still the same today? So understanding the above concepts is beneficial for learning JS. Let&amp;rsquo;s encourage each other!&lt;/p></description></item><item><title>On Sync, Async, and Cross-Origin</title><link>https://en.1991421.cn/2018/11/04/sync-async-cors/</link><pubDate>Sun, 04 Nov 2018 22:04:33 +0800</pubDate><guid>https://en.1991421.cn/2018/11/04/sync-async-cors/</guid><description>&lt;blockquote>
&lt;p>A recent project combined two concepts—async calls and cross-origin navigation—so I jotted down the reasoning.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-04-140150.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="scenario">
&lt;a class="heading-anchor-link" href="#scenario">Scenario&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scenario"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Site A must post data to site B and then land on a page served by B. The integration team on B’s side told us the endpoint returns a full HTML document whose JavaScript performs the redirect. We have to consume that response as-is.&lt;/p>
&lt;h2 id="options-considered">
&lt;a class="heading-anchor-link" href="#options-considered">Options considered&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="options-considered"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="async-request">
&lt;a class="heading-anchor-link" href="#async-request">Async request?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="async-request"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>An async XHR/Fetch call would immediately run into CORS, which we could solve, but we’d then receive a blob of HTML. We’d have to open a window, inject that HTML, and let it execute. The response references third-party scripts, so parsing and running it ourselves is error-prone. Not ideal.&lt;/p>
&lt;h3 id="chosen-approach">
&lt;a class="heading-anchor-link" href="#chosen-approach">Chosen approach&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="chosen-approach"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>We submit a hidden form from site A to site B. B responds with the full HTML document, and the browser renders it natively. No manual parsing, no extra work.&lt;/p>
&lt;h2 id="why-this-works">
&lt;a class="heading-anchor-link" href="#why-this-works">Why this works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-this-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A simple problem, but it highlights a few browser rules worth remembering.&lt;/p>
&lt;h3 id="form-submissions-and-cross-origin">
&lt;a class="heading-anchor-link" href="#form-submissions-and-cross-origin">Form submissions and cross-origin&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="form-submissions-and-cross-origin"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Under the same-origin policy, XHR/Fetch requests can’t read cross-origin responses without explicit permission—otherwise site A could steal data from site B. Traditional form submissions are different: the browser sends the request and replaces the current document with the response. Site A never gets access to B’s data, so the browser treats it as safe. (See &lt;a href="https://developer.mozilla.org/zh-TW/docs/Web/Security/Same-origin_policy" target="_blank" rel="noopener">Same-origin policy&lt;/a>.)&lt;/p>
&lt;h3 id="sync-vs-async">
&lt;a class="heading-anchor-link" href="#sync-vs-async">Sync vs. async&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sync-vs-async"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>A form submit is synchronous: the current page halts while the request completes and the new page loads. Async requests, by contrast, let the page keep executing while the request runs in the background. (See &lt;a href="https://zh.wikipedia.org/wiki/AJAX" target="_blank" rel="noopener">AJAX&lt;/a> and &lt;a href="https://www.zhihu.com/question/28550867" target="_blank" rel="noopener">this explainer on async vs. multithreading vs. parallelism&lt;/a>.)&lt;/p>
&lt;h3 id="browser-parsing">
&lt;a class="heading-anchor-link" href="#browser-parsing">Browser parsing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-parsing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Because the browser treats the response as a full navigation, it parses and renders the returned HTML—including any scripts that redirect to the final destination.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-11-04-135916.jpg"
alt="Page"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes the simplest tool—a synchronous form submit—is the right answer. Front-end work is filled with small, nuanced rules; keep exploring and practicing.&lt;/p></description></item><item><title>Angular VS React [Translation]</title><link>https://en.1991421.cn/2018/10/28/angular-vs-react/</link><pubDate>Sun, 28 Oct 2018 22:41:41 +0800</pubDate><guid>https://en.1991421.cn/2018/10/28/angular-vs-react/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-030052.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;a href="https://medium.freecodecamp.org/a-comparison-between-angular-and-react-and-their-core-languages-9de52f485a76" target="_blank" rel="noopener">Original Article&lt;/a>&lt;/p>
&lt;p>In this article, we&amp;rsquo;ll compare the two most popular web technologies of 2018, exploring their history, core differences, and the core languages they use [TypeScript, JavaScript]. Overall, these technologies enable developers to modularize and componentize their code for reuse, modification, and maintenance.&lt;/p>
&lt;p>The purpose of this article is not to find the best technology, but rather to compare, highlight, and clarify some misconceptions. We also focus on what&amp;rsquo;s important to us in the long term, rather than trivial details.&lt;/p>
&lt;p>You should understand that comparing these two technologies doesn&amp;rsquo;t cover everything. Angular is a comprehensive MVC framework, while React is a library integrated with a collection of open-source packages.
Note! The technologies referred to here are Angular and React.&lt;/p>
&lt;h2 id="questions-to-address">
&lt;a class="heading-anchor-link" href="#questions-to-address">Questions to Address&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="questions-to-address"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Core differences between Angular and React&lt;/li>
&lt;li>What makes TypeScript special&lt;/li>
&lt;li>Why these technologies are popular&lt;/li>
&lt;li>Current state of open-source projects&lt;/li>
&lt;li>Which technology do major companies use most&lt;/li>
&lt;li>Do statically typed languages affect code quality and development cycles&lt;/li>
&lt;/ul>
&lt;p>The following content will provide answers based on these questions.&lt;/p>
&lt;h2 id="key-comparison">
&lt;a class="heading-anchor-link" href="#key-comparison">Key Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="key-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s a comparison: Angular (left), React (right)&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-081158.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Worth mentioning, React&amp;rsquo;s performance with virtual DOM is excellent, as you may have heard. If not, I&amp;rsquo;ll explain.&lt;/p>
&lt;h3 id="the-problem">
&lt;a class="heading-anchor-link" href="#the-problem">The Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Suppose in an HTML markup block, we want to update a user&amp;rsquo;s birth date&lt;/p>
&lt;h3 id="virtual-dom">
&lt;a class="heading-anchor-link" href="#virtual-dom">Virtual DOM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="virtual-dom"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Only updates the differences between the current HTML version and the previous one. This is similar to how GitHub detects file changes.&lt;/p>
&lt;h3 id="traditional-dom">
&lt;a class="heading-anchor-link" href="#traditional-dom">Traditional DOM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="traditional-dom"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Updates the entire HTML tree structure&lt;/p>
&lt;h2 id="whats-the-impact">
&lt;a class="heading-anchor-link" href="#whats-the-impact">What&amp;rsquo;s the Impact&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="whats-the-impact"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For the above requirement, no problem. But if on a page we handle 20-30 asynchronous data requests, and for each request we replace the entire HTML block, this will affect user experience.&lt;/p>
&lt;p>Need more context? See &lt;a href="https://medium.com/@hidace/understanding-reacts-virtual-dom-vs-the-real-dom-68ae29039951" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>Let&amp;rsquo;s go back to the beginning&lt;/p>
&lt;h2 id="history">
&lt;a class="heading-anchor-link" href="#history">History&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="history"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We need to know some history (context), which provides us with perspective that might affect the future.&lt;/p>
&lt;blockquote>
&lt;p>I won&amp;rsquo;t delve deeply into Angular vs AngularJS, there are many &lt;a href="https://www.angularjswiki.com/angularjs/history-of-angularjs/" target="_blank" rel="noopener">resources&lt;/a> on this. In short, Google replaced AngularJS with Angular, and JavaScript with TypeScript.&lt;/p>
&lt;/blockquote>
&lt;p>Back in the ES4/ES5 era, JavaScript had a really steep learning curve. If you came from the world of Java, C#, C++, the object-oriented OOP world, and then learned JavaScript, it was really unintuitive, in other words, quite painful.&lt;/p>
&lt;p>This wasn&amp;rsquo;t because the language was poorly written, but because the purposes were different. JavaScript&amp;rsquo;s purpose was for asynchronous operations, like user interactions, data binding, transitions/animations, etc. Different animals have different behaviors.&lt;/p>
&lt;h2 id="popularity">
&lt;a class="heading-anchor-link" href="#popularity">Popularity&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="popularity"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Angular and React are the two most popular web technologies in 2018.&lt;/p>
&lt;p>Angular has higher search volume than React, but this doesn&amp;rsquo;t mean React is worse than Angular. But it shows people are interested, and note that users might search for AngularJS or Angular, so the search volume is actually higher.&lt;/p>
&lt;p>What&amp;rsquo;s certain is: both technologies are evolving and have bright futures. But we also know what happened to AngularJS later, which might happen to React too. But that&amp;rsquo;s the nature of business, and so are we.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-141924.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Google Trends Angular vs React&lt;/p>
&lt;h2 id="open-source">
&lt;a class="heading-anchor-link" href="#open-source">Open Source&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-source"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React has over 100K stars, 1200 contributors, and nearly 300 issues awaiting resolution.
React has a time-to-market advantage because it&amp;rsquo;s been released for 3 years, which is longer than Angular. This means it has faced real-world problems and undergone rigorous testing, ultimately developing into a frontend library with both adaptability and flexibility.&lt;/p>
&lt;p>Angular has 6 times more issues, but let&amp;rsquo;s not forget that Angular is a framework, and since Angular was released in 2016, fewer developers are using it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-083818.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-technologies-do-major-companies-use">
&lt;a class="heading-anchor-link" href="#what-technologies-do-major-companies-use">What Technologies Do Major Companies Use&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-technologies-do-major-companies-use"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>React was originally developed by Facebook to optimize and decouple component development. &lt;a href="https://medium.com/@chriscordle" target="_blank" rel="noopener">An article&lt;/a> by Chris Cordle points out that React usage at Facebook is higher than Angular usage at Google.&lt;/p>
&lt;p>So who&amp;rsquo;s using these technologies?&lt;/p>
&lt;h3 id="react">
&lt;a class="heading-anchor-link" href="#react">React&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Facebook&lt;/li>
&lt;li>Airbnb&lt;/li>
&lt;li>Uber&lt;/li>
&lt;li>Netflix&lt;/li>
&lt;li>Instagram&lt;/li>
&lt;li>WhatsApp&lt;/li>
&lt;li>Dropbox&lt;/li>
&lt;/ul>
&lt;h3 id="angular">
&lt;a class="heading-anchor-link" href="#angular">Angular&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="angular"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Eat24&lt;/li>
&lt;li>CVS shop&lt;/li>
&lt;li>onefootball&lt;/li>
&lt;li>Google Express&lt;/li>
&lt;li>NBA&lt;/li>
&lt;li>Delta&lt;/li>
&lt;/ul>
&lt;h2 id="typescript-and-javascript-es6">
&lt;a class="heading-anchor-link" href="#typescript-and-javascript-es6">TypeScript and JavaScript (ES6+)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript-and-javascript-es6"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Comparing only Angular and React without focusing on core languages would be misleading.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-085755.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>In terms of user base, JavaScript is larger, but TypeScript is growing, so who knows what the next 10-15 years will bring.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-142152.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Google trends — TypeScript and JavaScript&lt;/p>
&lt;p>TypeScript was originally developed by Microsoft to simplify JavaScript. Released in 2012, it&amp;rsquo;s a compiler, which means you can write ES5 code in TypeScript files.&lt;/p>
&lt;p>Generally speaking, TypeScript provides a smooth transition for developers with object-oriented backgrounds. Note that TypeScript was released during the ES5 era, when ES5 wasn&amp;rsquo;t yet an object-oriented language.&lt;/p>
&lt;p>In short, you could implement object-orientation and classes through prototype chains. But for developers with OOP backgrounds, this was difficult. So when you choose TypeScript, it feels familiar and comfortable.&lt;/p>
&lt;p>However, over the past few years, JavaScript has undergone massive changes, like modules, classes, spread operators, arrow functions, template strings, etc. In summary, this allows developers to write declarative code that supports OOP language features.&lt;/p>
&lt;h2 id="static-vs-dynamic-languages">
&lt;a class="heading-anchor-link" href="#static-vs-dynamic-languages">Static vs Dynamic Languages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="static-vs-dynamic-languages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>A static language means you can define variable types (String, Number, Array, etc.). You might ask, why is this important?
If you want to fill up with gasoline, one important thing is using the right fuel. If you don&amp;rsquo;t know, you might need to buy a new car.
Of course, the severity differs from programming, but in some cases it can be similar. Think about it: if you&amp;rsquo;re developing a large application, you want to know parameter types, otherwise you&amp;rsquo;ll break the code.
Well, if you&amp;rsquo;re still confused, look here.&lt;/p>
&lt;h3 id="static-type-properties">
&lt;a class="heading-anchor-link" href="#static-type-properties">Static Type Properties&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="static-type-properties"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-105507.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Static typed property comparison between JavaScript and TypeScript&lt;/p>
&lt;h3 id="static-type-arguments">
&lt;a class="heading-anchor-link" href="#static-type-arguments">Static Type Arguments&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="static-type-arguments"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-105559.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Static typed argument comparison between JavaScript and TypeScript&lt;/p>
&lt;p>I&amp;rsquo;ve learned that most developers believe statically typed languages mean more reliable code, and are better than dynamically typed languages most of the time. Frankly, it&amp;rsquo;s hard to prove this point because it depends on the development environment, developer experience, and project requirements.&lt;/p>
&lt;h3 id="survey-results">
&lt;a class="heading-anchor-link" href="#survey-results">Survey Results&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="survey-results"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Statically typed languages require more time to fix type issues&lt;/li>
&lt;li>Dynamically typed languages are easier to read and write
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-142938.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;/ul>
&lt;p>The survey indicates that on average, when developers use dynamically typed languages, they can save development time with minor modifications.
If you want to dive deeper into this topic, you can read this article &lt;a href="https://medium.com/@_ericelliott" target="_blank" rel="noopener">which states that you may not need TypeScript (or statically typed languages).&lt;/a>&lt;/p>
&lt;h2 id="which-to-choose">
&lt;a class="heading-anchor-link" href="#which-to-choose">Which to Choose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="which-to-choose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>So the topic is not only about Angular and React, but also focuses on which language you&amp;rsquo;re willing to invest time in. Honestly, this isn&amp;rsquo;t very important.
But if we had to choose, it seems TypeScript doesn&amp;rsquo;t have many advantages over JavaScript. I don&amp;rsquo;t understand why people would ignore ES6+. Anyway, if you&amp;rsquo;re not a typing enthusiast, there&amp;rsquo;s no resistance to writing ES6 code in .ts files. It&amp;rsquo;s just available if you need it.&lt;/p>
&lt;p>Here&amp;rsquo;s a simple class declaration example&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-105913.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>TypeScript&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-10-28-105921.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>JavaScript (ES6)&lt;/p>
&lt;h2 id="in-my-opinion">
&lt;a class="heading-anchor-link" href="#in-my-opinion">In My Opinion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="in-my-opinion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Static typing is more structured, secure, readable, and easier to collaborate with (avoiding passing unexpected values). However, with dynamic typing development, we have flexibility and creativity, focusing on development without worrying too much about types, interfaces, generics, etc.&lt;/p>
&lt;p>In the past web apps I&amp;rsquo;ve built, I didn&amp;rsquo;t use static typing and didn&amp;rsquo;t have any major problems. This doesn&amp;rsquo;t mean I don&amp;rsquo;t like static typing, just that I didn&amp;rsquo;t need it yet, but maybe I will in the future.&lt;/p>
&lt;h2 id="additional-points">
&lt;a class="heading-anchor-link" href="#additional-points">Additional Points&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="additional-points"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>React effectively handles memory management&lt;/li>
&lt;li>React uses JavaScript (ES6), ES6 was released in 1995&lt;/li>
&lt;li>Angular uses TypeScript, TypeScript was released in 2012&lt;/li>
&lt;li>Statically typed languages are great, but you can do well without them&lt;/li>
&lt;li>Dynamically typed languages can take less time and offer more flexibility&lt;/li>
&lt;li>If you&amp;rsquo;ve always worked with dynamically typed languages, learning statically typed languages will be a challenge&lt;/li>
&lt;li>TS is ES6+ plus types and some other features&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Your choice of framework or library might affect the time you spend programming and your budget. If you have a team of C#, Java, or C++ developers, I&amp;rsquo;d suggest choosing Angular because TypeScript has so many similarities to these languages.
The best recommendation is to initialize an application with both Angular and React, then evaluate the language and workflow before making a decision.
As mentioned earlier, all technologies have their own advantages and similarities, and it really depends on what requirements the application presents.
If you want to understand the web ecosystem:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://medium.freecodecamp.org/learn-these-core-javascript-concepts-in-just-a-few-minutes-f7a16f42c1b0" target="_blank" rel="noopener">Core Web Concepts&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.freecodecamp.org/7-javascript-methods-that-will-boost-your-skills-in-less-than-8-minutes-4cc4c3dca03f" target="_blank" rel="noopener">Important JavaScript Methods&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://codeburst.io/learn-how-to-create-custom-bash-commands-in-less-than-4-minutes-6d4ceadd9590" target="_blank" rel="noopener">Creating Custom Bash Commands (Linux/MacOS Developers)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Simplify OpenConnect with Shell + expect</title><link>https://en.1991421.cn/2018/09/15/shellopenconnect/</link><pubDate>Sat, 15 Sep 2018 13:02:33 +0800</pubDate><guid>https://en.1991421.cn/2018/09/15/shellopenconnect/</guid><description>&lt;blockquote>
&lt;p>I often need to VPN into the company from home. The repeated steps are tedious, so I wrote a small shell/expect script to streamline it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="previous-operation">
&lt;a class="heading-anchor-link" href="#previous-operation">Previous Operation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="previous-operation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-09-15-044531.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>As shown, every time I had to enter the OpenConnect command, a hard‑to‑remember IP, and go through 5 interactive prompts. Time to automate it with a shell script.&lt;/p>
&lt;h2 id="shell-automation">
&lt;a class="heading-anchor-link" href="#shell-automation">Shell automation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="shell-automation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Except for the final OTP/password (which I still enter manually), all other inputs can be scripted. I use &lt;a href="https://linux.die.net/man/1/expect" target="_blank" rel="noopener">expect&lt;/a> to handle the interactive prompts.&lt;/p>
&lt;h3 id="install-expect">
&lt;a class="heading-anchor-link" href="#install-expect">Install expect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-expect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Run &lt;code>brew install expect&lt;/code>.&lt;/p>
&lt;h3 id="script">
&lt;a class="heading-anchor-link" href="#script">Script&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="script"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Save as &lt;code>vpn.sh&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/usr/bin/expect
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># vpn to xxx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">spawn sudo openconnect 1.1.1.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">expect &lt;span class="s2">&amp;#34;Password&amp;#34;&lt;/span> &lt;span class="o">{&lt;/span>send &lt;span class="s2">&amp;#34;xxxxxxxx\r&amp;#34;&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">expect &lt;span class="s2">&amp;#34;Enter &amp;#39;yes&amp;#39; to accept&amp;#34;&lt;/span> &lt;span class="o">{&lt;/span>send &lt;span class="s2">&amp;#34;yes\r&amp;#34;&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">expect &lt;span class="s2">&amp;#34;Username:&amp;#34;&lt;/span> &lt;span class="o">{&lt;/span>send &lt;span class="s2">&amp;#34;xxx\r&amp;#34;&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">expect &lt;span class="s2">&amp;#34;Password&amp;#34;&lt;/span> &lt;span class="o">{&lt;/span>send &lt;span class="s2">&amp;#34;xxx\r&amp;#34;&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">interact
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Done. To connect:&lt;/p>
&lt;ol>
&lt;li>&lt;code>./vpn.sh&lt;/code>&lt;/li>
&lt;li>Enter the one‑time code manually&lt;/li>
&lt;/ol>
&lt;p>Much faster.&lt;/p>
&lt;h2 id="reference">
&lt;a class="heading-anchor-link" href="#reference">Reference&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://segmentfault.com/a/1190000002564816" target="_blank" rel="noopener">About shell, expect, and ssh&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Babel vs. TSC</title><link>https://en.1991421.cn/2018/08/11/babeltsc/</link><pubDate>Sat, 11 Aug 2018 20:31:35 +0800</pubDate><guid>https://en.1991421.cn/2018/08/11/babeltsc/</guid><description>&lt;blockquote>
&lt;p>React projects typically compile with Babel; Angular (non-AngularJS) projects rely on TSC. I used both casually for years and still stepped into pits—&lt;code>Object.assign&lt;/code>, &lt;code>Promise&lt;/code>, lookbehind regex, etc. Time to understand them properly.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-08-11-112400.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-compilers-transform">
&lt;a class="heading-anchor-link" href="#what-compilers-transform">What compilers transform&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-compilers-transform"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;strong>Definitions&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;em>Babel is a JavaScript compiler. Use next generation JavaScript, today.&lt;/em>&lt;/li>
&lt;li>&lt;em>TypeScript is a superset of JavaScript that compiles to clean JavaScript output.&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>Straight from their docs. Compilers take what we write and output target code. Crucially, &lt;strong>they only compile syntax—they do not polyfill new APIs such as &lt;code>Promise&lt;/code>, &lt;code>Symbol&lt;/code>, or global methods like &lt;code>Object.assign&lt;/code>.&lt;/strong> They can’t conjure what isn’t there.&lt;/p>
&lt;h3 id="react--babel">
&lt;a class="heading-anchor-link" href="#react--babel">React + Babel&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="react--babel"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When the browser lacks those APIs, ship polyfills.&lt;/p>
&lt;h4 id="babel-polyfill">&lt;code>babel-polyfill&lt;/code>&lt;/h4>&lt;ol>
&lt;li>Install &lt;a href="https://github.com/babel/babel/tree/master/packages/babel-polyfill" target="_blank" rel="noopener">babel-polyfill&lt;/a>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm i babel-polyfill --save-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>In webpack, prepend it to the entry:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">entry&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;babel-polyfill&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;./app/js&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>Simple, but increases bundle size because it loads everything.&lt;/li>
&lt;/ol>
&lt;h4 id="babel-runtime">&lt;code>babel-runtime&lt;/code>&lt;/h4>&lt;ol>
&lt;li>Install the runtime plugin and &lt;code>core-js&lt;/code>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm i babel-plugin-transform-runtime --save-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm i core-js --save-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>Update Babel config:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">query: {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> plugins: [
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;transform-runtime&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> presets: [&amp;#39;es2015&amp;#39;, &amp;#39;stage-0&amp;#39;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>Now only the constructs you use (e.g., &lt;code>Promise&lt;/code>) are bundled, keeping size down at the cost of slightly slower builds.&lt;/li>
&lt;/ol>
&lt;h4 id="on-demand-polyfills">On-demand polyfills&lt;/h4>&lt;p>If your audience spans multiple browsers and versions, preloading everything wastes bandwidth for modern browsers. In one Angular + Express project I served polyfills conditionally based on user agent (screenshot in the original post).&lt;/p>
&lt;h3 id="angular-with-tsc">
&lt;a class="heading-anchor-link" href="#angular-with-tsc">Angular with TSC&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="angular-with-tsc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Similar ideas apply—import &lt;code>core-js&lt;/code> in &lt;code>polyfills.ts&lt;/code> or load them conditionally.&lt;/p>
&lt;h2 id="why-compilers-help">
&lt;a class="heading-anchor-link" href="#why-compilers-help">Why compilers help&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-compilers-help"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With compilers we can adopt modern language features without waiting for browser support, boosting productivity and maintainability. Remember, ES evolves annually now.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We often chase trendy tech like TypeScript or React and treat Babel/TSC as transparent build steps. But without the compilers, the fancy syntax wouldn’t run anywhere. Understand them and you’ll avoid many headaches.&lt;/p></description></item><item><title>What's New in Node.js 10</title><link>https://en.1991421.cn/2018/07/24/node10/</link><pubDate>Tue, 24 Jul 2018 21:58:08 +0800</pubDate><guid>https://en.1991421.cn/2018/07/24/node10/</guid><description>&lt;blockquote>
&lt;p>Every Node.js developer is caught in the version vortex. If you stop for a while, you&amp;rsquo;ll be several versions behind. I used to work with Node v6, and now it&amp;rsquo;s already v10 - scary, isn&amp;rsquo;t it? Some say &amp;ldquo;frontend difficulty doubles every eighteen months&amp;rdquo; - I believe that. So for new things, it&amp;rsquo;s best to actively keep up. Node v10 has been released for some time, so here&amp;rsquo;s a translated article introducing its highlights.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://ws2.sinaimg.cn/large/006tKfTcly1ftkxbekhq8j30m805z0t3.jpg"
alt="Node.js"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Original article URL: &lt;a href="https://levelup.gitconnected.com/whats-new-in-node-10-ad360ae55ee4" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;p>Node.js v10 was released on April 24, 2018, and will enter &lt;a href="https://en.wikipedia.org/wiki/Long-term_support" target="_blank" rel="noopener">Long Term Support (LTS)&lt;/a> in October. Let&amp;rsquo;s look at some notable features in this release.&lt;/p>
&lt;h3 id="added-error-codes">
&lt;a class="heading-anchor-link" href="#added-error-codes">Added Error Codes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="added-error-codes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Error messages in Node have been standardized.&lt;/p>
&lt;p>In the past, handling errors was a headache. Previous errors only contained string information, and if we wanted to perform operations based on specific error messages, the only way was string matching.&lt;/p>
&lt;p>Because error handling required additional string matching, even the smallest updates couldn&amp;rsquo;t be added to the next major version without breaking semantic versioning. Decoupling error information allows developers to improve error messages without introducing breaking changes. To learn more, &lt;a href="https://medium.com/the-node-js-collection/node-js-errors-changes-you-need-to-know-about-dc8c82417f65" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="n-api-is-no-longer-experimental">
&lt;a class="heading-anchor-link" href="#n-api-is-no-longer-experimental">N-API is No Longer Experimental&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="n-api-is-no-longer-experimental"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Node documentation introduces N-API as an API for building native addons. This is an API independent of the JavaScript runtime and maintained as part of Node.js. This API is application binary interface (ABI) stable across versions of Node.js. It aims to isolate addons from changes in the underlying JavaScript engine and allows modules compiled for one version to run on later versions of Node.js without recompilation.&lt;/p>
&lt;p>N-API was experimentally introduced in Node 8 and is stable in version 10. Node version updates will no longer cause module breakage, and it also introduces compatibility between Node v6.x and 8.x.&lt;/p>
&lt;h3 id="native-node-http2-more-stable">
&lt;a class="heading-anchor-link" href="#native-node-http2-more-stable">Native Node HTTP/2 More Stable&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="native-node-http2-more-stable"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Node 8 introduced the experimental HTTP/2 module, which is a significant update. HTTP/2 enhances the standard HTTP protocol.&lt;/p>
&lt;ul>
&lt;li>Multiplexing&lt;/li>
&lt;li>Single connection&lt;/li>
&lt;li>Server push&lt;/li>
&lt;li>Prioritization&lt;/li>
&lt;li>Header compression
Ending the experimental phase, the native HTTP/2 module will enable Node servers to provide better web experiences.&lt;/li>
&lt;/ul>
&lt;h3 id="v8-engine-v66-performance-improvements">
&lt;a class="heading-anchor-link" href="#v8-engine-v66-performance-improvements">V8 Engine v6.6 Performance Improvements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="v8-engine-v66-performance-improvements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Node ships with the V8 JavaScript engine, and Node.js v10 includes the latest version. For browsers, V8 v6.6 under Chrome 66 can reduce JavaScript parse and compile time by 20-40%. Therefore, we can expect Node 10 to benefit greatly in this area, while also providing async generators and array performance.&lt;/p>
&lt;p>With software, speed is crucial, and the latest Node version focuses on improving these. To learn more, &lt;a href="https://v8project.blogspot.com/2018/04/improved-code-caching.html" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="better-es-module-esm-support">
&lt;a class="heading-anchor-link" href="#better-es-module-esm-support">Better ES Module (ESM) Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="better-es-module-esm-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ESM
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="nx">pkg&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;./pkg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">vs&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// CJS
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">pkg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./pkg&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Although we haven&amp;rsquo;t seen full ES module support in Node 10 yet, the Node team is still working on it, and this will continue to develop.&lt;/p>
&lt;p>Node previously used CommonJS (CJS), and a new module system called ECMAScript Modules (ESM). Node has been moving in this direction.&lt;/p>
&lt;p>Integrating ESM into Node isn&amp;rsquo;t a simple matter because it conflicts with the current system. However, Node is committed to finding a solution. &lt;a href="https://medium.com/u/c845cd91bc98" target="_blank" rel="noopener">Gil Tayar&lt;/a> wrote an article around this topic. Interested? &lt;a href="https://medium.com/@giltayar/native-es-modules-in-nodejs-status-and-future-directions-part-i-ee5ea3001f71" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;h3 id="diagnostic-tracing-improvements">
&lt;a class="heading-anchor-link" href="#diagnostic-tracing-improvements">Diagnostic Tracing Improvements&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="diagnostic-tracing-improvements"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Event tracing has been added for better visibility. This new feature can be used to improve timing and performance issue analysis. The API allows users to turn events on or off and has the capability to diagnose problems on demand.&lt;/p>
&lt;h3 id="npm-v6-arrives">
&lt;a class="heading-anchor-link" href="#npm-v6-arrives">npm v6 Arrives&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="npm-v6-arrives"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>npm recently upgraded from v5.7 to v6.0, and Node 10 follows this update. npm improvements focus on performance, security, and stability. To learn more about v6, &lt;a href="https://medium.com/npm-inc/announcing-npm-6-5d0b1799a905" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h3 id="openssl-upgraded-to-110">
&lt;a class="heading-anchor-link" href="#openssl-upgraded-to-110">OpenSSL Upgraded to 1.1.0&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="openssl-upgraded-to-110"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Node comes with modern cryptographic support, including the newly added ChaCha20 encryption and Poly1305 authentication. TLS 1.3 was recently finalized, and when Node 10 enters LTS in October, it will fully support this standard.&lt;/p>
&lt;h3 id="fs-functions-have-experimental-promise-support">
&lt;a class="heading-anchor-link" href="#fs-functions-have-experimental-promise-support">&amp;lsquo;fs&amp;rsquo; Functions Have Experimental Promise Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fs-functions-have-experimental-promise-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Interacting with the file system is a primary function of many Node applications. Node 10 will add an experimental Promise version to the fs package. Previously these functions handled asynchronous operations through callbacks, but could be converted using the util.promisify function that came with Node 8. Now developers can use promises with fs without any additional steps.&lt;/p>
&lt;h4 id="summary">Summary&lt;/h4>&lt;p>The Node team continues to push new features, enabling developers to build better experiences for users. Node 10 will bring cutting-edge technology in the future, improving error handling, building native addons, and some other obvious enhancements.&lt;/p>
&lt;p>Node 10 will enter LTS from October 2018 until April 2021. The Node team follows a special odd-even release cycle. While entering LTS, Node 11 will also be released in October. Odd-numbered versions mean experimental features, while even-numbered versions are LTS versions. This will also mark the deprecation of Node 4 long-term support.&lt;/p>
&lt;p>Happy JavaScripting!&lt;/p></description></item><item><title>Commonly Used Docker Commands</title><link>https://en.1991421.cn/2018/07/07/docker/</link><pubDate>Sat, 07 Jul 2018 10:36:48 +0800</pubDate><guid>https://en.1991421.cn/2018/07/07/docker/</guid><description>&lt;blockquote>
&lt;p>Here I&amp;rsquo;ll record frequently used Docker commands for future reference&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-07-07-024107.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="docker">
&lt;a class="heading-anchor-link" href="#docker">Docker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># For logging into custom sources, add domain registry address&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker login &lt;span class="o">[&lt;/span>x.xxx.com&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View Docker version, status, proxy information, etc.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check Docker service status on Linux&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service docker status
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Start Docker service on Linux&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service docker start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View all containers (including those not running)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker ps -a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Delete all containers&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm -f &lt;span class="k">$(&lt;/span>docker ps -aq&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create a container, -P means Docker randomly maps a port to the internal container&amp;#39;s open port, -p 8888:80 means specify port mapping to container port 80&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker run -p 8888:80 -d --name &lt;span class="s2">&amp;#34;hellonginx&amp;#34;&lt;/span> nginx:latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Enter Docker container bash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it containerId bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Stop container&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker stop containerId
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Delete container&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rm contaierId
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View all images&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker images
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Delete all images&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker rmi -f &lt;span class="k">$(&lt;/span>docker images -aq&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create image, can specify output location with -o, note that multiple tags can be specified, such as -t t1 -t t2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker build -t toolkit:v1 .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Push image, supports including repository address, such as private source address&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker push toolkit:v1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View logs, useful when container doesn&amp;#39;t start normally&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker logs containerId --timestamps
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Output image file as tar package, image ID, image name/TAG all work&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker save &amp;lt;imageId&amp;gt; -o &amp;lt;filename&amp;gt;.tar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Update container restart policy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker update --restart&lt;span class="o">=&lt;/span>always my-container
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="docker-compose">
&lt;a class="heading-anchor-link" href="#docker-compose">docker-compose&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docker-compose"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Official website: &lt;a href="https://docs.docker.com/compose/reference/" target="_blank" rel="noopener">https://docs.docker.com/compose/reference/&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Start orchestration service, (default: docker-compose.yml), note if -f is set, it must be at the beginning, don&amp;#39;t change parameter order&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose -f marvel-docker-compose.yml up -d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Recreate containers --force-recreate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker-compose up -d --force-recreate &lt;span class="o">[&lt;/span>service&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="common-issues-explained">
&lt;a class="heading-anchor-link" href="#common-issues-explained">Common Issues Explained&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues-explained"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="-p-parameter">
&lt;a class="heading-anchor-link" href="#-p-parameter">-p parameter&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="-p-parameter"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Write the port mapping completely, such as &lt;code>-p 8888:80&lt;/code>, don&amp;rsquo;t use abbreviated form&lt;/p>
&lt;p>Port format is &lt;code>host port:container port or HOST:CONTAINER&lt;/code>&lt;/p>
&lt;p>For port configuration of each container in docker-compose, follow the same format&lt;/p>
&lt;h3 id="build-supports-url">
&lt;a class="heading-anchor-link" href="#build-supports-url">build supports URL&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-supports-url"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Sometimes when building images for private repositories, you need to add the URL for build and push, like &lt;code>docker build github.com/creack/docker-firefox&lt;/code>&lt;/p>
&lt;h3 id="stop-and-delete-target-container">
&lt;a class="heading-anchor-link" href="#stop-and-delete-target-container">Stop and delete target container&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="stop-and-delete-target-container"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker ps -q --filter &lt;span class="s2">&amp;#34;name=hellonginx&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep -q . &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> docker stop hellonginx &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> docker rm -fv hellonginx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="error-permission-denied">
&lt;a class="heading-anchor-link" href="#error-permission-denied">Error: Permission denied&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="error-permission-denied"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For example, if you get permission issues when reading after starting nginx with Docker, it&amp;rsquo;s because the Docker mounted disk doesn&amp;rsquo;t have sufficient permissions. The solution is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">chmod -R 777 volume/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Why does this permission issue occur? Because when Docker runs, the system user for external access is Docker, not root, so there&amp;rsquo;s a possibility of insufficient permissions. You can check the permission situation of the corresponding disk directory.&lt;/p>
&lt;h3 id="accessing-host-services-from-inside-the-container">
&lt;a class="heading-anchor-link" href="#accessing-host-services-from-inside-the-container">Accessing host services from inside the container&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="accessing-host-services-from-inside-the-container"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>You can&amp;rsquo;t directly access 127.0.0.1; you need to configure the network for normal access&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2022/2022-05-03-000011.png"
alt="https://static.1991421.cn/2022/2022-05-03-000011.png"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h4 id="docker-compose-1">docker-compose&lt;/h4>&lt;p>Configure as follows, and use host.docker.internal instead of local IP address for access&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">extra_hosts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;host.docker.internal:host-gateway&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dockerignorecopy-blacklist">
&lt;a class="heading-anchor-link" href="#dockerignorecopy-blacklist">.dockerignore/Copy blacklist&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dockerignorecopy-blacklist"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If you don&amp;rsquo;t want to copy files into the image, you can solve this by configuring a blacklist through .dockerignore&lt;/p>
&lt;p>&lt;a href="https://docs.docker.com/engine/reference/builder/#dockerignore-file" target="_blank" rel="noopener">https://docs.docker.com/engine/reference/builder/#dockerignore-file&lt;/a>&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above commands may change or become invalid; always refer to the official CLI as the definitive resource, &lt;a href="https://docs.docker.com/engine/reference/run/" target="_blank" rel="noopener">click here&lt;/a>&lt;/p></description></item><item><title>Migrating Blog Services to a Domestic Server</title><link>https://en.1991421.cn/2018/07/06/migrate-blog-to-china/</link><pubDate>Fri, 06 Jul 2018 19:25:04 +0800</pubDate><guid>https://en.1991421.cn/2018/07/06/migrate-blog-to-china/</guid><description>&lt;blockquote>
&lt;p>Previously, my blog&amp;rsquo;s static pages and blog management platform were hosted on a Bandwagon Host VPS abroad, and the access speed was very slow, especially for the backend management platform, where operations had noticeable delays. This time, I decided to migrate the services to a domestic server on Tencent Cloud.&lt;/p>
&lt;/blockquote>
&lt;p>Since the entire project hasn&amp;rsquo;t been Dockerized yet, the environment had to be reconfigured from scratch, which took about an hour [Nginx, Node, SSL, etc.], and I couldn&amp;rsquo;t help but feel the inconvenience of not having Docker. . . . Next time, I must figure it out.&lt;/p>
&lt;p>After more than an hour of effort, the service was finally successfully migrated to China. I tried accessing it from my phone and computer, and the speed improved significantly.&lt;/p>
&lt;p>Because I had just deployed it, I used ~~ &lt;a href="https://www.17ce.com/" target="_blank" rel="noopener">https://www.17ce.com/&lt;/a> ~~ for website speed testing, but the test unexpectedly indicated that my IP was from the UK. That was frustrating—I clearly bought a Tencent Cloud server in the Chengdu region. Several testing tools showed the same result. However, the access speed was indeed very fast, which was not scientific. So I had to contact customer service via a work order, and the answer I got was that the IP data of the testing tool I used was not up-to-date. They recommended a tool, which I&amp;rsquo;m sharing here. If you need to query a site&amp;rsquo;s IP, use this: &lt;strong>&lt;a href="https://www.ipip.net/ip.html" target="_blank" rel="noopener">https://www.ipip.net/ip.html&lt;/a>&lt;/strong>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-07-06-113758.png"
alt="Migrating Blog Services to a Domestic Server - Figure 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If your web service&amp;rsquo;s main audience is in China, it is recommended to host it domestically. Foreign VPS is indeed cheap, but the access speed is still slow.&lt;/p></description></item><item><title>[Translation] Promises vs Observables</title><link>https://en.1991421.cn/2018/07/01/promises-vs-observables/</link><pubDate>Sun, 01 Jul 2018 11:16:47 +0800</pubDate><guid>https://en.1991421.cn/2018/07/01/promises-vs-observables/</guid><description>&lt;blockquote>
&lt;p>When developing Angular 2+ projects, I&amp;rsquo;ve always used RxJS for asynchronous handling. Angular is highly coupled with RxJS and closely follows its development. By Angular 6, the dependent RxJS version had already been upgraded to version 6.&lt;/p>
&lt;/blockquote>
&lt;p>Admittedly, RxJS isn&amp;rsquo;t strictly necessary for development - Promises work too. However, you&amp;rsquo;ll find that RxJS offers more flexibility, richness, and elegance. That said, RxJS has a steep learning curve. In the book &amp;ldquo;RxJS in Depth,&amp;rdquo; it&amp;rsquo;s described as a cliff.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-07-02-035724.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;em>Image from the book&lt;/em>&lt;/p>
&lt;p>To understand and learn RxJS, I believe comparing it with Promises is an important approach. I recently read a great article that might help everyone get a glimpse of the differences.&lt;/p>
&lt;hr>
&lt;p>Original link: &lt;a href="https://medium.com/@mpodlasin/promises-vs-observables-4c123c51fe13" target="_blank" rel="noopener">Promises vs Observables&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>Observables continue to evolve. Angular 2 uses them as the default asynchronous processing solution. You can use their middleware in your React-Redux applications. But why and when should you use them? This article will explain the biggest differences between the two. Understanding these can help you decide which to choose. Even if you ultimately don&amp;rsquo;t choose Observables, I hope you&amp;rsquo;ll learn more about Promises.&lt;/p>
&lt;/blockquote>
&lt;p>Note that this article describes how Observables work under RxJS. Other reactive programming libraries may differ.&lt;/p>
&lt;p>The article assumes you already have basic knowledge of &lt;code>Promises&lt;/code>. On the other hand, if &lt;code>Observables&lt;/code> are new to you, this article can serve as an introduction. But don&amp;rsquo;t stop here - Observables have much more to offer. In particular, we barely mention operators, which are heavily used in RxJS and reactive programming.&lt;/p>
&lt;h2 id="single-value-vs-multiple-values">
&lt;a class="heading-anchor-link" href="#single-value-vs-multiple-values">Single Value vs Multiple Values&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="single-value-vs-multiple-values"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Promises are widely used for handling HTTP requests. In this model, you make a request and wait for a response. You can be sure that the same request won&amp;rsquo;t have multiple responses.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">numberPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">numberPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// will simply print 5
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But trying to resolve the Promise with another value will fail. Promises always handle the value from the first resolve function call and ignore subsequent calls.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">numberPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">numberPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// still prints only 5
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In contrast, Observables allow you to handle (or we call it &amp;ldquo;emit&amp;rdquo;) multiple values. Look at this code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">numberObservable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">numberObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// prints 5 and 10
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice the familiar syntax? We changed Promise to Observable, resolve to observer.next, and then to subscribe - how similar!&lt;/p>
&lt;p>This behavior is actually Observables&amp;rsquo; biggest selling point. When you think about browser asynchronous sources, you quickly think of single request-single response models that work fine with single requests or setTimeout timers. But there are also these scenarios:&lt;/p>
&lt;ul>
&lt;li>setInterval&lt;/li>
&lt;li>WebSockets&lt;/li>
&lt;li>DOM events (mouse clicks, etc.)&lt;/li>
&lt;li>Any other type of events (this issue also exists in Node.js)&lt;/li>
&lt;/ul>
&lt;p>Although we&amp;rsquo;ve made some progress with Promises later on, we still end up using terrible callbacks. Doesn&amp;rsquo;t anyone realize we&amp;rsquo;ve only solved part of the problem? Thanks to the RxJS authors for their work.&lt;/p>
&lt;p>Let&amp;rsquo;s see how to wrap setInterval with an Observable:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">secondsObservable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">secondsObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// logs:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// and so on, every second
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To avoid triggering anonymous undefined values, we initialize the counter &lt;code>i&lt;/code>, then every second we execute &lt;code>observer.next&lt;/code> to pass the i value.&lt;/p>
&lt;p>This is an example of an Observable that actually never stops emitting values. So instead of getting a single value from a promise, you get an input that could be any number of values.&lt;/p>
&lt;h2 id="eager-vs-lazy">
&lt;a class="heading-anchor-link" href="#eager-vs-lazy">Eager vs Lazy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="eager-vs-lazy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s assume Promises support emitting multiple values, and let&amp;rsquo;s rewrite the &lt;code>setInterval&lt;/code> example using Promise.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">secondsPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We have a problem here: even if no one is listening to these values (we haven&amp;rsquo;t even logged them), &lt;code>setInterval&lt;/code> is still called immediately when the Promise is created. We&amp;rsquo;re wasting resources, triggering these values because no one is listening to them. This happens because Promises are eager. You can easily test this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">promise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;I was called!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above code will immediately print &lt;code>&amp;quot;I was called!&amp;quot;&lt;/code> to the terminal. In contrast, the Observable-based code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">observable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;I was called!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This doesn&amp;rsquo;t happen. This is because Promises are eager, while Observables are lazy. The function passed to the Observable constructor is only invoked when someone actually subscribes to an Observable.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">observable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// just now &amp;#34;I was called!&amp;#34; gets printed
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This might seem like a small change, but let&amp;rsquo;s go back to the Observable-wrapped &lt;code>setInterval&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">secondsObservable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Because of laziness, &lt;code>setInterval&lt;/code> is not called at this time, and even the &lt;code>i&lt;/code> variable isn&amp;rsquo;t initialized. The function passed to the Observable is just waiting until someone actually subscribes to this Observable.&lt;/p>
&lt;p>To summarize this point: initializing a Promise means the process has already started (the HTTP request has been sent), we&amp;rsquo;re just waiting for the result. This is because the function issued the request the moment the Promise was created. On the other hand, initializing an Observable represents a potential request, but it only triggers when we actually subscribe, potentially saving browser resources by avoiding work that nobody cares about.&lt;/p>
&lt;h2 id="non-cancellable-vs-cancellable">
&lt;a class="heading-anchor-link" href="#non-cancellable-vs-cancellable">Non-cancellable vs Cancellable&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="non-cancellable-vs-cancellable"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s assume Promises are lazy. Imagine in our Promise example, we only call &lt;code>setInterval&lt;/code> when someone is listening to the result. But what if someone stops listening? You might know that setInterval returns a token that can be used to cancel the timer. We should be able to do this when consumers no longer want to listen to events, because resources shouldn&amp;rsquo;t be wasted when no one is listening.&lt;/p>
&lt;p>Actually, some Promise libraries support this. &lt;a href="http://bluebirdjs.com/docs/getting-started.html" target="_blank" rel="noopener">Bluebird Promises&lt;/a> support cancellation methods that you can use on a Promise to stop what&amp;rsquo;s happening inside. Let&amp;rsquo;s see how to do this to cancel these operations.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">secondsPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onCancel&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">onCancel&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">clearInterval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">token&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The key is we pass the &lt;code>onCancel&lt;/code> function, a special callback that can be called when the user decides to cancel the Promise. Cancellation basically works like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">logSecondsPromise&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">secondsPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// we print values every second
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// (in our imaginary version of Promises),
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// but at some point user calls:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">logSecondsPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cancel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// from this moment numbers are no longer logged
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that we cancel the promise from then, with the side effect that values are printed to the console.&lt;/p>
&lt;p>Let&amp;rsquo;s see how to cancel with Observables:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">secondsObservable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">clearInterval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">token&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compared to cancellable promises, not much has changed. Instead of passing a function to &lt;code>onCancel&lt;/code>, we just return it.&lt;/p>
&lt;p>Cancelling (or, we call it unsubscribing) Observables looks similar:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">subscription&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">secondsObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">subscription&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unsubscribe&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice that subscribe doesn&amp;rsquo;t return an Observable! This means you can&amp;rsquo;t chain multiple subscribes like you can with then in promises. Subscribe just returns a subscription to a given Observable. This subscription has only one method: unsubscribe. You unsubscribe when you decide you no longer want to listen to this Observable.&lt;/p>
&lt;p>If you&amp;rsquo;re worried about the lack of chained observables making Observables non-reusable, remember there are plenty of operators, and operators do support chaining.&lt;/p>
&lt;p>On the other hand, if you&amp;rsquo;re concerned about verbose subscription handling, there are operators that let you handle this elegantly. In fact, every operator lets you handle this smartly, ensuring you don&amp;rsquo;t subscribe to things you don&amp;rsquo;t need.&lt;/p>
&lt;p>Enough talk, let&amp;rsquo;s continue. Although some Promise libraries support cancellation, ES6 Promises do not. There was a proposal to add cancellation functionality to Promise, but it was rejected. There are still other ways to cancel Promises, but if you compare the languages themselves, you&amp;rsquo;ll find Observable wins because it was designed with cancellation from the beginning.&lt;/p>
&lt;h2 id="multicast-vs-unicast-or-multicast">
&lt;a class="heading-anchor-link" href="#multicast-vs-unicast-or-multicast">Multicast vs Unicast or Multicast&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="multicast-vs-unicast-or-multicast"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>However, there&amp;rsquo;s another issue with lazy Promises: even if the function is passed to the constructor, it&amp;rsquo;s only called when someone calls then. What if someone calls then a few minutes later? Should we call this function again, or share the previous result?&lt;/p>
&lt;p>Because Promises are not lazy, they naturally implement the second approach - the function passed to the Promise constructor is only called when the Promise is created. This behavior works well for HTTP requests. Consider this simple example that returns after a 1-second delay:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">waitOneSecondPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Of course, real Promises start computing immediately, but if they were lazy, they should only compute when the user actually needs them.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">waitOneSecondPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomething&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>doSomething will be called after a 1-second wait. Everything is fine, unless another user also wants to use the same Promise:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">waitOneSecondPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomething&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 500ms passes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">waitOneSecondPromise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomethingElse&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The user naturally expects doSomethingElse to also be called after 1 second, but it will be called after only half a second. Why? Because someone used it before, the function passed to the Promise constructor was called, so setTimeout started and the 1-second countdown began. When we call the second function, half the time has already passed. Because we share the timer, they will be called at the same time, not after 1 second each.&lt;/p>
&lt;p>Let&amp;rsquo;s modify the Promise to log something:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">waitOneSecondPromise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;I was called!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the previous example, even if then is called a second time, you&amp;rsquo;ll only see one &amp;ldquo;I was called!&amp;rdquo;, which proves there&amp;rsquo;s only one setTimeout timer instance.&lt;/p>
&lt;p>This is more obvious in a special case. If a Promise function were to separately call &lt;code>then&lt;/code> for each user, &lt;code>setTimeout&lt;/code> would be called separately, ensuring their callbacks execute as expected. In fact, this is exactly what Observables do. Let&amp;rsquo;s rewrite this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">waitOneSecondObservable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;I was called&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setTimeout&lt;/span>&lt;span class="p">(()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Each time we &lt;code>subscribe&lt;/code>, we start our own clock.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">waitOneSecondObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomething&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 500 ms
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">waitOneSecondObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomethingElse&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Both &lt;code>doSomething&lt;/code> and &lt;code>doSomethingElse&lt;/code> functions will execute 1 second after being subscribed. If you look at the console, you&amp;rsquo;ll see &amp;ldquo;I was called&amp;rdquo; printed twice. This shows that the function passed to the Observable constructor was actually called twice, and setTimeout timers were created twice.&lt;/p>
&lt;p>However, it&amp;rsquo;s worth mentioning that this isn&amp;rsquo;t always the behavior you want. HTTP requests are something you want to execute only once, but you want to share the result with many subscribers. Observables don&amp;rsquo;t do this by default, but they can support it, and it&amp;rsquo;s very simple - you just need to use the &lt;code>share&lt;/code> operator.&lt;/p>
&lt;p>Assuming the previous example, we want to call both &lt;code>doSomething&lt;/code> and &lt;code>doSomethingElse&lt;/code> simultaneously, regardless of when we pass them to subscribe, roughly like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">sharedWaitOneSecondObservable&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">waitOneSecondObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">share&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">sharedWaitOneSecondObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomething&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 500 ms passes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">sharedWaitOneSecondObservable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">doSomethingElse&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If an Observable shares results among subscribers, we say it&amp;rsquo;s &amp;ldquo;multicast&amp;rdquo; because it delivers a single value to multiple entities. By default, Observables are unicast, meaning each result is delivered to a single, unique subscriber.&lt;/p>
&lt;p>So we understand that Observables win again in flexibility: Promises (because they&amp;rsquo;re eager, not lazy) are always &amp;ldquo;multicast&amp;rdquo;, while Observables are unicast by default but can easily be converted to multicast when necessary.&lt;/p>
&lt;h2 id="always-asynchronous-vs-possibly-asynchronous">
&lt;a class="heading-anchor-link" href="#always-asynchronous-vs-possibly-asynchronous">Always Asynchronous vs Possibly Asynchronous&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="always-asynchronous-vs-possibly-asynchronous"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Let&amp;rsquo;s go back to this very simple example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">promise&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that in a function, we resolve synchronously. Because we already have the value, we immediately execute the Promise, ensuring that when the user calls then, the callback function can immediately process this synchronously? Well, no. In fact, it&amp;rsquo;s always asynchronous. This can be clearly seen here:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;!&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;And now we are here.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>First &amp;ldquo;And now we are here.&amp;rdquo; is printed, then &amp;ldquo;5!&amp;rdquo;, even though the Promise has already resolved that number.&lt;/p>
&lt;p>Observables, in contrast, actually emit values synchronously:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">observable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">observer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">observer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">observable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;!&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;And now we are here.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&amp;ldquo;5!&amp;rdquo; will appear first, then &amp;ldquo;And now we are here.&amp;rdquo;. Of course, we can delay emitting the value, for example by wrapping &amp;ldquo;observer.next(5)&amp;rdquo; in setTimeout. So we see that Observables are more flexible. You might think this behavior is dangerous because &amp;ldquo;subscribe&amp;rdquo; doesn&amp;rsquo;t work as expected, but I mentioned that RxJS has many ways to implement event listening asynchronously (if interested, check out the &lt;code>observeOn&lt;/code> operator).&lt;/p>
&lt;h2 id="conclusion">
&lt;a class="heading-anchor-link" href="#conclusion">Conclusion&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="conclusion"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That&amp;rsquo;s it! If you have other good examples to illustrate the differences between Promises and Observables, please let me know in the comments. What about similarities?&lt;/p>
&lt;p>I hope after reading this article, you have the ability to choose which solution to use in your projects.&lt;/p>
&lt;p>Hint: Both might work!&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This translation provides a comprehensive comparison between Promises and Observables, highlighting their key differences in handling asynchronous operations. Understanding these distinctions is crucial for making informed decisions about which approach to use in different scenarios, particularly in modern JavaScript development with frameworks like Angular that heavily leverage RxJS.&lt;/p></description></item><item><title>[Translation] TypeScript vs. JavaScript: Should You Migrate Your Project to TypeScript?</title><link>https://en.1991421.cn/2018/06/30/typescript-vs-javascript-should-you-migrate-your-project-to-typescript/</link><pubDate>Sat, 30 Jun 2018 22:09:37 +0800</pubDate><guid>https://en.1991421.cn/2018/06/30/typescript-vs-javascript-should-you-migrate-your-project-to-typescript/</guid><description>&lt;p>Original link: &lt;a href="https://stackify.com/typescript-vs-javascript-migrate/" target="_blank" rel="noopener">TypeScript vs. JavaScript: Should You Migrate Your Project to TypeScript?&lt;/a>&lt;/p>
&lt;blockquote>
&lt;p>In September 2016, the first official Angular 2 release recommended TypeScript. As a result, many Angular developers chose TS. Other frameworks like React and Vue also support TS now. TS is hot, but why is it good? How does it differ from JS, and what pain points does it solve? I saw a great article and translated it here in case it helps.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-07-01-025045.jpg"
alt="TypeScript vs. JavaScript - Fig 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>In programming, TypeScript (TS) and JavaScript (JS) are both popular. But what are the differences, and when should each be used? This article compares them, discussing differences, pros, and cons.&lt;/p>
&lt;h2 id="what-is-typescript">
&lt;a class="heading-anchor-link" href="#what-is-typescript">What is TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>TypeScript is a superset of JavaScript that compiles to JavaScript (EcmaScript 3+). It provides type annotations for optional static type checking at compile time. Because it is a superset, all JS syntax is valid in TS. But that does not mean all JS will pass the TS compiler.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// throws: error TS2322: Type &amp;#39;1&amp;#39; is not assignable to type &amp;#39;string&amp;#39;.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="typescript-advantages">
&lt;a class="heading-anchor-link" href="#typescript-advantages">TypeScript advantages&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript-advantages"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="type-annotations">
&lt;a class="heading-anchor-link" href="#type-annotations">Type annotations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="type-annotations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>TypeScript was created for &amp;ldquo;static type analysis.&amp;rdquo; It allows us to create safe declarations. Compare this JS and TS function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Basic JavaScript
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">getPassword&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">clearTextPassword&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">clearTextPassword&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;********&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">password&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">getPassword&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;false&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// &amp;#34;password&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>JavaScript cannot prevent invalid arguments, and runtime does not error. In TypeScript, you can avoid this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Written with TypeScript
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">getPassword&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">clearTextPassword&lt;/span>: &lt;span class="kt">boolean&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">clearTextPassword&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;********&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">password&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">getPassword&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;false&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// throws: error TS2345: Argument of type &amp;#39;&amp;#34;false&amp;#34;&amp;#39; is not assignable to parameter of type &amp;#39;boolean&amp;#39;.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This shows how TS prevents calls with unexpected types. Historically, one JS problem is lack of type checking, which causes tracking issues for people unfamiliar with JS complexity.&lt;/p>
&lt;h3 id="language-features">
&lt;a class="heading-anchor-link" href="#language-features">Language features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="language-features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Besides static type analysis, TS adds these features:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.tslang.cn/docs/handbook/interfaces.html" target="_blank" rel="noopener">Interfaces&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.tslang.cn/docs/handbook/generics.html" target="_blank" rel="noopener">Generics&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.tslang.cn/docs/handbook/namespaces.html" target="_blank" rel="noopener">Namespaces&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.tslang.cn/docs/release-notes/typescript-2.0.html" target="_blank" rel="noopener">Null checks&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.tslang.cn/docs/handbook/classes.html" target="_blank" rel="noopener">Access modifiers&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="api-documentation">
&lt;a class="heading-anchor-link" href="#api-documentation">API documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="api-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If getPassword(&amp;hellip;) belongs to a library, how do we know its types? We can use jsdoc, IDEs like VSCode, or docs like Dash. But none provides the TS experience.
Take the fetch API example. The screenshot shows VSCode Peek Definition. With these tools, you can see argument and return types. This is better than traditional JS + jsdoc.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-07-01-032550.jpg"
alt="TypeScript vs. JavaScript - Fig 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="misconceptions">
&lt;a class="heading-anchor-link" href="#misconceptions">Misconceptions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="misconceptions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There are misconceptions for people choosing TS:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ES6 features&lt;/strong>: Some choose TS for ES6 features like modules, classes, arrow functions. But Babel can handle this. It&amp;rsquo;s not common to see TS and Babel together.&lt;/li>
&lt;li>&lt;strong>Easier than JavaScript&lt;/strong>: This is subjective. Some argue TS introduces syntax noise. The key is that TS doesn&amp;rsquo;t hide JS. It is a superset and does not protect against issues unrelated to static typing (this, scope, prototypes, etc.). Developers still need strong JS fundamentals.&lt;/li>
&lt;li>&lt;strong>Type correctness == program correctness&lt;/strong>: This is not true, but static types provide a safety net. If type correctness doesn&amp;rsquo;t imply program correctness, how do we ensure correctness? Unit tests. Tests can catch most errors.&lt;/li>
&lt;li>&lt;strong>Static typing supports tree shaking&lt;/strong>: Tree shaking removes dead code through static structures (named imports/exports, constants). TS currently does not support tree shaking by itself.&lt;/li>
&lt;/ul>
&lt;h2 id="syntax-and-compilation-comparison">
&lt;a class="heading-anchor-link" href="#syntax-and-compilation-comparison">Syntax and compilation comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="syntax-and-compilation-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>People choose TS because of modules and classes, but ES6 JS also supports these. You can transpile to ES5 for compatibility. Below are syntax comparisons for TS, its compiled output, and ES6 + Babel output.&lt;/p>
&lt;h3 id="classes">
&lt;a class="heading-anchor-link" href="#classes">Classes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="classes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- TypeScript -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">class&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- TypeScript compiled output -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="cm">/** @class */&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}());&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- JavaScript with Babel -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">class&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- Babel compiled output -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;use strict&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">_classCallCheck&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">instance&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Constructor&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">instance&lt;/span> &lt;span class="k">instanceof&lt;/span> &lt;span class="nx">Constructor&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">TypeError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Cannot call a class as a function&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_classCallCheck&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Modules&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="modules">
&lt;a class="heading-anchor-link" href="#modules">Modules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="modules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- TypeScript -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- TypeScript compiled output -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">define&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;require&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;exports&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">require&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">exports&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;use strict&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">defineProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">exports&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;__esModule&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">value&lt;/span>: &lt;span class="kt">true&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="cm">/** @class */&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">Article() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">exports&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">default&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- JavaScript with Babel -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- Babel compiled output -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;use strict&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">defineProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">exports&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;__esModule&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">_classCallCheck&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">instance&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Constructor&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">instance&lt;/span> &lt;span class="k">instanceof&lt;/span> &lt;span class="nx">Constructor&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">TypeError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Cannot call a class as a function&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">Article&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">_classCallCheck&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">exports&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">default&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Article&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="optional-params">
&lt;a class="heading-anchor-link" href="#optional-params">Optional params&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="optional-params"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- TypeScript -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>: &lt;span class="kt">string&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- TypeScript compiled output -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- JavaScript with Babel -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="nx">Log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// -- Babel compiled output -- //
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;use strict&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">Log&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">arguments&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">arguments&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="kc">undefined&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">arguments&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="when-to-choose-typescript-vs-javascript">
&lt;a class="heading-anchor-link" href="#when-to-choose-typescript-vs-javascript">When to choose TypeScript vs JavaScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-to-choose-typescript-vs-javascript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="typescript">
&lt;a class="heading-anchor-link" href="#typescript">TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>You want type checking at compile time, rather than runtime checks in vanilla JS.&lt;/li>
&lt;li>New library/framework development. For example, starting a React project where you are not familiar with the API. With type definitions, you get better IntelliSense and navigation to learn APIs quickly.&lt;/li>
&lt;li>Large projects or teams. TypeScript interfaces and access modifiers are valuable for collaboration.&lt;/li>
&lt;/ul>
&lt;h3 id="javascript">
&lt;a class="heading-anchor-link" href="#javascript">JavaScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="javascript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Build tooling requirements&lt;/strong>: TypeScript adds a build step. But fewer projects now avoid build tools.&lt;/li>
&lt;li>&lt;strong>Small projects&lt;/strong>: TS may be overkill for small teams or small codebases.&lt;/li>
&lt;li>&lt;strong>Robust testing workflow&lt;/strong>: If you already have test-driven development, switching to TS might cost more than it yields.&lt;/li>
&lt;li>&lt;strong>Extra dependencies&lt;/strong>: To use TS libraries, you need type definitions. Each definition is another npm package with maintenance risk. That can reduce TS benefits. Note: &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped" target="_blank" rel="noopener">DefinitelyTyped&lt;/a> mitigates this; most popular types are there.&lt;/li>
&lt;li>&lt;strong>Framework support&lt;/strong>: If your framework does not support TS, you cannot use its features. For example &lt;a href="https://github.com/emberjs/ember.js" target="_blank" rel="noopener">EmberJS&lt;/a> (though it is planned; TS is a language for &lt;a href="https://glimmerjs.com/" target="_blank" rel="noopener">Glimmer&lt;/a>).&lt;/li>
&lt;/ul>
&lt;h2 id="other-considerations">
&lt;a class="heading-anchor-link" href="#other-considerations">Other considerations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-considerations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>So you decided to introduce a type system. Is TS the only choice? No. There are two mainstream alternatives worth considering:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/facebook/flow" target="_blank" rel="noopener">FaceBook&amp;rsquo;s Flow&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developers.google.com/closure/compiler/" target="_blank" rel="noopener">Google&amp;rsquo;s Closure&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How to Import Third-Party JavaScript in Angular Projects</title><link>https://en.1991421.cn/2018/06/30/angularjs/</link><pubDate>Sat, 30 Jun 2018 09:29:50 +0800</pubDate><guid>https://en.1991421.cn/2018/06/30/angularjs/</guid><description>&lt;blockquote>
&lt;p>In actual Angular project development, you may need to import third-party JavaScript libraries, such as &lt;a href="https://github.com/dankogai/js-base64" target="_blank" rel="noopener">base64.js&lt;/a>, a base64 encoder.&lt;/p>
&lt;/blockquote>
&lt;p>How do we do this?&lt;/p>
&lt;h2 id="importing-the-js-library">
&lt;a class="heading-anchor-link" href="#importing-the-js-library">Importing the JS Library&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="importing-the-js-library"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">npm install --save js-base64
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="installing-type-declaration-files">
&lt;a class="heading-anchor-link" href="#installing-type-declaration-files">Installing Type Declaration Files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-type-declaration-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev @types/js-base64
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="importing-in-components">
&lt;a class="heading-anchor-link" href="#importing-in-components">Importing in Components&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="importing-in-components"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">Base64&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;js-base64&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">HomeComponent&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">OnInit&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Base64&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;dankogai&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As shown above, you can use js-base64 normally. It&amp;rsquo;s not difficult, but there are a few points to clarify.&lt;/p>
&lt;h2 id="what-are-types">
&lt;a class="heading-anchor-link" href="#what-are-types">What are @types?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-types"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>TypeScript is a superset of JavaScript. Compared to JavaScript, its most critical feature is static type checking (Type Guard). However, JavaScript itself doesn&amp;rsquo;t have static type checking functionality, and the TypeScript compiler only provides type declarations for standard libraries in the ECMAScript standard, and can only recognize types in TypeScript code.&lt;/li>
&lt;li>TypeScript declaration files are TypeScript code files with a .d.ts suffix, but their purpose is to describe type information for all exported interfaces in a JavaScript module (in the broad sense).&lt;/li>
&lt;li>The @types package we install here is the declaration file for js-base64. &lt;em>It&amp;rsquo;s precisely because of this that the js-base64 object type can be recognized, and we can see that the IDE recognizes its functions like encode()&lt;/em>.&lt;/li>
&lt;/ul>
&lt;h2 id="where-does-js-base64-go-after-import-and-bundling">
&lt;a class="heading-anchor-link" href="#where-does-js-base64-go-after-import-and-bundling">Where does js-base64 go after import and bundling?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="where-does-js-base64-go-after-import-and-bundling"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>With this import method, during build and bundling, js-base64 will be included in the ng project files. If the system doesn&amp;rsquo;t use lazy loading, it will be in main.js. If lazy loading is implemented, it will be included in the corresponding chunk.js file of the module that imports and uses it.&lt;/p>
&lt;h2 id="is-the-types-approach-the-only-solution">
&lt;a class="heading-anchor-link" href="#is-the-types-approach-the-only-solution">Is the @types approach the only solution?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-the-types-approach-the-only-solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>NO! We can also do this:&lt;/p>
&lt;ol>
&lt;li>Import the corresponding JS resource in the index.html file&lt;/li>
&lt;li>Declare at the top of the component that uses it: &lt;code>declare const Base64: any;&lt;/code>
This also works, but the downside is that it doesn&amp;rsquo;t leverage TypeScript&amp;rsquo;s static type checking, so there are pros and cons.&lt;/li>
&lt;/ol>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.tslang.cn/docs/handbook/declaration-files/consumption.html" target="_blank" rel="noopener">Declaration File Usage&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/angular/angular-cli/wiki/stories-third-party-lib" target="_blank" rel="noopener">Stories Third Party Lib&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://my.oschina.net/fenying/blog/748805" target="_blank" rel="noopener">Using and Writing TypeScript Declaration Files&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cannot find module inherits</title><link>https://en.1991421.cn/2018/06/26/7a3a5e4/</link><pubDate>Tue, 26 Jun 2018 02:51:28 +0800</pubDate><guid>https://en.1991421.cn/2018/06/26/7a3a5e4/</guid><description>&lt;blockquote>
&lt;p>Recently, while upgrading an Angular 5 project to 6, I hit a CI build error after pushing code: &lt;code>Error: Cannot find module 'inherits'&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>Online suggestions include reinstalling Node, updating NPM, or installing the missing package. But after installing the package, the build fails again with other errors, for example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Cannot find module &amp;#39;semver&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Error: Cannot find module &amp;#39;semver&amp;#39;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So installing packages directly is not the right solution.&lt;/p>
&lt;p>After analysis, it turned out to be an issue with the lock file (added in npm5 to ensure versions). &lt;strong>Delete the lock file and rerun &lt;code>npm i&lt;/code>&lt;/strong>. It will regenerate the lock file, and the build passes.&lt;/p>
&lt;p>Below is the lock file before and after. Comparing them shows the issue was indeed there.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-26-072603.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>The left image is the lock file that caused the build error.&lt;/li>
&lt;li>The right image is the regenerated one.&lt;/li>
&lt;/ul>
&lt;h2 id="package-lock-notes">
&lt;a class="heading-anchor-link" href="#package-lock-notes">package-lock notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="package-lock-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If you change package.json and it differs from the lock file, then running &lt;code>npm i&lt;/code> will download the latest packages based on the versions and semantic ranges in package.json, and update the lock file. If both are in sync, &lt;code>npm i&lt;/code> will install strictly based on the lock file and will not consider whether newer versions exist.&lt;/p></description></item><item><title>My First Experience with Docker</title><link>https://en.1991421.cn/2018/06/24/docker/</link><pubDate>Sun, 24 Jun 2018 05:04:36 +0800</pubDate><guid>https://en.1991421.cn/2018/06/24/docker/</guid><description>&lt;blockquote>
&lt;p>Docker has been a hot topic in recent years, and many companies have already widely adopted this technology. If you don&amp;rsquo;t know about it, it might seem a bit behind the times, so it&amp;rsquo;s worth spending some time to understand it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="introduction-to-docker">
&lt;a class="heading-anchor-link" href="#introduction-to-docker">Introduction to Docker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="introduction-to-docker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-24-121810.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>Docker is an open-source software project that automates the deployment of applications in software containers. It provides an additional software abstraction layer on Linux operating systems and an automated management mechanism for operating system-level virtualization [1]. Docker uses resource isolation mechanisms in the Linux kernel, such as cgroups, and Linux kernel namespaces, to create independent software containers. These can operate under a single Linux entity, avoiding the additional overhead of starting a virtual machine [2]. The Linux kernel&amp;rsquo;s support for namespaces completely isolates the application&amp;rsquo;s view of the working environment, including process trees, networks, user IDs, and mounted file systems, while the kernel&amp;rsquo;s cgroups provide resource isolation, including CPU, memory, block I/O, and network. Starting from version 0.9, Docker began including the libcontainer library as its own way to directly use the virtualization facilities provided by the Linux kernel, building upon the abstraction provided by libvirt&amp;rsquo;s LXC and systemd-nspawn interfaces.
According to industry analysis firm &amp;ldquo;451 Research&amp;rdquo;: &amp;ldquo;Docker is a tool that can package applications and their dependencies into virtual containers that can run on any Linux server. This helps achieve flexibility and portability, allowing applications to run anywhere, whether in public cloud, private cloud, standalone machines, etc.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Excerpt from Wikipedia, &lt;a href="https://zh.m.wikipedia.org/zh-hans/Docker_%28%E8%BB%9F%E9%AB%94%29" target="_blank" rel="noopener">View original&lt;/a>&lt;/p>
&lt;h2 id="dockers-capabilities">
&lt;a class="heading-anchor-link" href="#dockers-capabilities">Docker&amp;rsquo;s Capabilities&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dockers-capabilities"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Docker provides a &amp;ldquo;one-stop&amp;rdquo; container solution for application development and deployment. It helps developers efficiently and quickly build applications, achieving &amp;ldquo;Build, Ship and Run Any App, Anywhere,&amp;rdquo; thus reaching the goal of &amp;ldquo;build once, run anywhere.&amp;rdquo;
Using Docker, we can:&lt;/p>
&lt;ul>
&lt;li>Faster delivery and deployment of application environments&lt;/li>
&lt;li>More efficient resource utilization&lt;/li>
&lt;li>More convenient migration and scalability&lt;/li>
&lt;li>Easier application update management&lt;/li>
&lt;/ul>
&lt;h2 id="getting-started---creating-a-mysql-container">
&lt;a class="heading-anchor-link" href="#getting-started---creating-a-mysql-container">Getting Started - Creating a MySQL Container&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-started---creating-a-mysql-container"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I discovered that Docker can solve environment problems. Thinking about the various development environment configuration and version issues I&amp;rsquo;ve encountered in the past, I realized that using Docker could make things much better. For example, in my development work, I need to use MySQL, but different projects may require different versions. I can&amp;rsquo;t install multiple versions on my computer, and if I install it system-wide, running it as a service for long periods also consumes system resources. This is indeed a problem.&lt;/p>
&lt;p>Using Docker seems to be a quick solution. So I downloaded and installed it, and got started.&lt;/p>
&lt;h3 id="downloading-docker">
&lt;a class="heading-anchor-link" href="#downloading-docker">Downloading Docker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="downloading-docker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I decisively downloaded it from the official website for safety and convenience. For Mac, it&amp;rsquo;s a DMG file that you download, then click to run and install.&lt;/p>
&lt;h3 id="starting-docker">
&lt;a class="heading-anchor-link" href="#starting-docker">Starting Docker&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="starting-docker"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Click to run and start Docker, execute &lt;code>docker images&lt;/code>, and see that there are no images yet. Pull from the hub:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker pull mysql:5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker pull mysql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-24-124624.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
As shown above, I pulled the latest version (8) and 5.x of MySQL, Redis, and some other commonly used database images
Execute &lt;code>$ docker run -p 3306:3306 --name mysql5 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5&lt;/code>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-24-125034.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
CLI usage instructions can be found here (&lt;a href="https://docs.docker.com/engine/reference/commandline/docker/" target="_blank" rel="noopener">Official Documentation&lt;/a>)&lt;/p>
&lt;p>After successful startup, I tried connecting to MySQL with Navicat - no problem. With just one line of code, MySQL was successfully installed.&lt;/p>
&lt;ul>
&lt;li>If I want to uninstall this, I just need to stop it, then remove it&lt;/li>
&lt;li>If MySQL 8 is released and I want to try it immediately, no problem - just run a container with the MySQL 8 image
Environment installation is that simple.&lt;/li>
&lt;/ul>
&lt;h3 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This is just a preliminary attempt, but it has already solved some of my development environment problems. More usage and exploration will continue.&lt;/p></description></item><item><title>hexo-error</title><link>https://en.1991421.cn/2018/06/20/hexo-error/</link><pubDate>Wed, 20 Jun 2018 08:17:24 +0800</pubDate><guid>https://en.1991421.cn/2018/06/20/hexo-error/</guid><description>&lt;blockquote>
&lt;p>My personal blog uses Hexo, which is simple but can still encounter some minor issues. Here are some common errors.&lt;/p>
&lt;/blockquote>
&lt;h2 id="yamlexception-can-not-read-a-block-mapping-entry-a-multiline-key-may-not-be-an-implicit-key-at-line-2-column-1">
&lt;a class="heading-anchor-link" href="#yamlexception-can-not-read-a-block-mapping-entry-a-multiline-key-may-not-be-an-implicit-key-at-line-2-column-1">YAMLException: can not read a block mapping entry; a multiline key may not be an implicit key at line 2, column 1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="yamlexception-can-not-read-a-block-mapping-entry-a-multiline-key-may-not-be-an-implicit-key-at-line-2-column-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This is essentially a syntax error, for example like this:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-20-121857.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Running &lt;code>hexo g&lt;/code> will report the above error because of the &lt;code>[]&lt;/code> brackets, so you need to add single quotes &lt;code>''&lt;/code>:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-20-121958.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Run it again and it should work.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Hexo is a powerful static site generator, but like any tool, it can encounter syntax and configuration issues. Understanding common YAML errors and their solutions can save time and frustration when working with Hexo. Always remember to properly quote special characters in YAML frontmatter to avoid parsing errors.&lt;/p></description></item><item><title>[Translation] Bootstrap 3 vs 4</title><link>https://en.1991421.cn/2018/06/20/boostrap3vs4/</link><pubDate>Wed, 20 Jun 2018 06:48:15 +0800</pubDate><guid>https://en.1991421.cn/2018/06/20/boostrap3vs4/</guid><description>&lt;blockquote>
&lt;p>Bootstrap released v4 on Jan 19. It introduced important changes, added new components, and removed some. Here are the differences between Bootstrap 3 and 4.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-21-114624.png"
alt="[Translation] Bootstrap 3 vs 4 - Fig 1"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Original link: &lt;a href="https://www.quackit.com/bootstrap/bootstrap_4/differences_between_bootstrap_3_and_bootstrap_4.cfm" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;h2 id="component-differences">
&lt;a class="heading-anchor-link" href="#component-differences">Component differences&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-differences"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Component&lt;/th>
&lt;th style="text-align: left">Bootstrap 3&lt;/th>
&lt;th style="text-align: left">Bootstrap 4&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Global&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">CSS source files&lt;/td>
&lt;td style="text-align: left">LESS&lt;/td>
&lt;td style="text-align: left">SCSS&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Main CSS units&lt;/td>
&lt;td style="text-align: left">px&lt;/td>
&lt;td style="text-align: left">rem&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Media query units&lt;/td>
&lt;td style="text-align: left">px&lt;/td>
&lt;td style="text-align: left">px&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Base font size&lt;/td>
&lt;td style="text-align: left">14px&lt;/td>
&lt;td style="text-align: left">16px&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Default font stack&lt;/td>
&lt;td style="text-align: left">Helvetica Neue, Helvetica, Arial, sans-serif&lt;/td>
&lt;td style="text-align: left">Uses a &amp;ldquo;native font stack&amp;rdquo; (user&amp;rsquo;s system fonts), with fallback to Helvetica Neue, Arial, and sans-serif&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Grid&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Grid breakpoints&lt;/td>
&lt;td style="text-align: left">4 breakpoints (xs, sm, md, lg)&lt;/td>
&lt;td style="text-align: left">5 breakpoints (xs, sm, md, lg, xl) &lt;em>Bootstrap 4 removed the smallest breakpoint xs, so col-&lt;/em> applies to all devices and you no longer need to specify sizes for that case*&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Offsets&lt;/td>
&lt;td style="text-align: left">Use col-&lt;em>-offset-&lt;/em> e.g. col-md-offset-4&lt;/td>
&lt;td style="text-align: left">Use offset-&lt;em>-&lt;/em> e.g. offset-md-4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Tables&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Dark tables&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;td style="text-align: left">Added dark table style with &lt;code>.table-dark&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Table header styles&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;td style="text-align: left">&lt;code>.thead-light&lt;/code> and &lt;code>.thead-dark&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Condensed tables&lt;/td>
&lt;td style="text-align: left">&lt;code>.table-condensed&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.table-sm&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">State classes&lt;/td>
&lt;td style="text-align: left">Bootstrap 3 uses &lt;code>.active&lt;/code> without &lt;code>.table-&lt;/code> prefix; Bootstrap 4 uses &lt;code>.table-active&lt;/code>. State keywords: active, success, info, warning, danger&lt;/td>
&lt;td style="text-align: left">Adds &lt;code>.table-&lt;/code> prefix for table state classes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Responsive tables&lt;/td>
&lt;td style="text-align: left">&lt;code>.table-responsive&lt;/code> must be on parent div&lt;/td>
&lt;td style="text-align: left">Same, plus &lt;code>table-responsive-*&lt;/code> for breakpoints &lt;code>.table-responsive-sm -md -lg -xl&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Forms&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Horizontal forms&lt;/td>
&lt;td style="text-align: left">Use &lt;code>form-horizontal&lt;/code>&lt;/td>
&lt;td style="text-align: left">Removed &lt;code>form-horizontal&lt;/code>. Use &lt;code>.row&lt;/code> and &lt;code>.form-row&lt;/code> for grid layout&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">Form layout uses &lt;code>.control-label&lt;/code> for grid&lt;/td>
&lt;td style="text-align: left">Use &lt;code>col-form-label-*&lt;/code> for grid labels&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Checkbox/radio&lt;/td>
&lt;td style="text-align: left">&lt;code>.radio&lt;/code>, &lt;code>.radio-inline&lt;/code>, &lt;code>.checkbox&lt;/code>, &lt;code>checkbox-inline&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.form-check&lt;/code>, &lt;code>.form-check-label&lt;/code>, &lt;code>.form-check-input&lt;/code>, &lt;code>.form-check-inline&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Form control sizes&lt;/td>
&lt;td style="text-align: left">&lt;code>.input-lg&lt;/code>, &lt;code>input-sm&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>form-control-lg&lt;/code>, &lt;code>form-control-sm&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Form label sizes&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;code>.col-form-label-sm&lt;/code>, &lt;code>.col-form-label-lg&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Help text&lt;/td>
&lt;td style="text-align: left">&lt;code>.help-block&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.form-text&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Validation &amp;amp; feedback icons&lt;/td>
&lt;td style="text-align: left">Includes validation styles, icons via glyphicons&lt;/td>
&lt;td style="text-align: left">Validation icons removed&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Legend titles&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;td style="text-align: left">Provides &lt;code>.col-form-label&lt;/code> for legend&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Static text&lt;/td>
&lt;td style="text-align: left">&lt;code>.form-control-static&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.form-control-plaintext&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Custom forms&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Buttons&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Styles&lt;/td>
&lt;td style="text-align: left">&lt;code>.btn-secondary&lt;/code> not available&lt;/td>
&lt;td style="text-align: left">Removed &lt;code>.btn-default&lt;/code>; added &lt;code>btn-secondary&lt;/code>, &lt;code>btn-light&lt;/code>, &lt;code>btn-dark&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Outline buttons&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;td style="text-align: left">&lt;code>btn-outline-*&lt;/code> adds border styles&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Button sizes&lt;/td>
&lt;td style="text-align: left">&lt;code>.btn-xs&lt;/code> available&lt;/td>
&lt;td style="text-align: left">&lt;code>.btn-xs&lt;/code> removed; &lt;code>.btn-sm&lt;/code>, &lt;code>.btn-lg&lt;/code> available&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Input groups&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Classes&lt;/td>
&lt;td style="text-align: left">Use &lt;code>.input-group-addon&lt;/code> and &lt;code>.input-group-btn&lt;/code>&lt;/td>
&lt;td style="text-align: left">Removed &lt;code>.input-group-addon&lt;/code>/&lt;code>input-group-btn&lt;/code>; added &lt;code>input-group-prepend&lt;/code>, &lt;code>.input-group-append&lt;/code>, and &lt;code>.input-group-text&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Media object&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Classes&lt;/td>
&lt;td style="text-align: left">&lt;code>.media&lt;/code>, &lt;code>.media-body&lt;/code> etc&lt;/td>
&lt;td style="text-align: left">Media object can use flex utilities directly&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Dropdowns&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Structure&lt;/td>
&lt;td style="text-align: left">&lt;code>&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>&amp;lt;ul&amp;gt;&amp;lt;div&amp;gt;&lt;/code> also allowed&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Menu headers&lt;/td>
&lt;td style="text-align: left">&lt;code>.dropdown-header&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.dropdown-header&lt;/code> used on &lt;code>&amp;lt;h1&amp;gt;-&amp;lt;h2&amp;gt;&lt;/code>, no longer in &lt;code>&amp;lt;li&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Dividers&lt;/td>
&lt;td style="text-align: left">&lt;code>.divider&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.dropdown-divider&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Disabled items&lt;/td>
&lt;td style="text-align: left">&lt;code>.disabled&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.disabled&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Button groups&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Extra small&lt;/td>
&lt;td style="text-align: left">&lt;code>btn-group-xs&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.btn-group-xs&lt;/code> removed&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Navigation&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Inline nav&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;code>.nav-inline&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Navbar&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Colors&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;code>.navbar-light&lt;/code>, &lt;code>.navbar-dark&lt;/code>, and &lt;code>.bg-*&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Alignment&lt;/td>
&lt;td style="text-align: left">&lt;code>.navbar-right&lt;/code>, &lt;code>.navbar-left&lt;/code>&lt;/td>
&lt;td style="text-align: left">Use spacing and flex utilities&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Navbar forms&lt;/td>
&lt;td style="text-align: left">&lt;code>.navbar-form&lt;/code>&lt;/td>
&lt;td style="text-align: left">Removed &lt;code>.navbar-form&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Fixed navbar&lt;/td>
&lt;td style="text-align: left">&lt;code>.navbar-fixed-top&lt;/code> and &lt;code>navbar-fixed-bottom&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>fixed-top&lt;/code>, &lt;code>fixed-bottom&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Pagination&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Default&lt;/td>
&lt;td style="text-align: left">&lt;code>.pagination&lt;/code>&lt;/td>
&lt;td style="text-align: left">Each &lt;code>&amp;lt;li&amp;gt;&lt;/code>/&lt;code>&amp;lt;a&amp;gt;&lt;/code> needs &lt;code>.page-item&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Prev/Next&lt;/td>
&lt;td style="text-align: left">&lt;code>.previous&lt;/code> and &lt;code>.next&lt;/code>&lt;/td>
&lt;td style="text-align: left">Removed&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Jumbotron&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Full width&lt;/td>
&lt;td style="text-align: left">&lt;code>.jumbotron&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.jumbotron-fluid&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Glyphicons&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Typography&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Quotes&lt;/td>
&lt;td style="text-align: left">Default &lt;code>&amp;lt;blockquote&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: left">Introduces &lt;code>.blockquote&lt;/code> on &lt;code>&amp;lt;blockquote&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Alignment&lt;/td>
&lt;td style="text-align: left">&lt;code>.block-reverse&lt;/code> for right&lt;/td>
&lt;td style="text-align: left">Use &lt;code>.text-center&lt;/code> and &lt;code>.text-right&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Page header&lt;/td>
&lt;td style="text-align: left">&lt;code>page-header&lt;/code> supported&lt;/td>
&lt;td style="text-align: left">&lt;code>page-header&lt;/code> not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Description lists&lt;/td>
&lt;td style="text-align: left">&lt;code>.dl-horizonal&lt;/code> for horizontal lists&lt;/td>
&lt;td style="text-align: left">Use &lt;code>.row&lt;/code> on &lt;code>&amp;lt;dl&amp;gt;&lt;/code> and grid on &lt;code>&amp;lt;dt&amp;gt;&amp;lt;dd&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Non-responsive usage&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">List Groups&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Link/button lists&lt;/td>
&lt;td style="text-align: left">&lt;code>.list-group-item&lt;/code> on &lt;code>&amp;lt;a&amp;gt;&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.list-group-item-action&lt;/code> on &lt;code>&amp;lt;a&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Collapse&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Shown content&lt;/td>
&lt;td style="text-align: left">&lt;code>.in&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.show&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Cards&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Panels&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Wells&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Thumbnails&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Breadcrumbs&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Class&lt;/td>
&lt;td style="text-align: left">Use &lt;code>.breadcrumb&lt;/code>&lt;/td>
&lt;td style="text-align: left">Also require &lt;code>.breadcrumb-item&lt;/code> on &lt;code>&amp;lt;li&amp;gt;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Carousel&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Carousel item&lt;/td>
&lt;td style="text-align: left">&lt;code>.item&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>.carousel-item&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Affix&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;td style="text-align: left">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Support&lt;/td>
&lt;td style="text-align: left">Supported&lt;/td>
&lt;td style="text-align: left">Not supported&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="browser-support">
&lt;a class="heading-anchor-link" href="#browser-support">Browser support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-27-040213.png"
alt="[Translation] Bootstrap 3 vs 4 - Fig 2"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above lists key differences; for full details, see the official docs.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://getbootstrap.com/docs/4.0/getting-started/introduction/" target="_blank" rel="noopener">Official docs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/twbs/bootstrap" target="_blank" rel="noopener">GitHub repo&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Building a Hexo-Based Blogging Platform</title><link>https://en.1991421.cn/2018/06/18/hexo/</link><pubDate>Mon, 18 Jun 2018 11:16:48 +0800</pubDate><guid>https://en.1991421.cn/2018/06/18/hexo/</guid><description>&lt;blockquote>
&lt;p>Hexo is a Node.js-based static blog framework that deploys nicely to GitHub Pages. Markdown is fast, but the surrounding workflow felt clunky.&lt;/p>
&lt;/blockquote>
&lt;p>Previously both source and static site lived in GitHub. Posting a new article meant:&lt;/p>
&lt;ol>
&lt;li>Open an editor (VS Code, Vim, WebStorm…)&lt;/li>
&lt;li>Run &lt;code>hexo new 'postname'&lt;/code>&lt;/li>
&lt;li>Write content&lt;/li>
&lt;li>Commit &amp;amp; push: &lt;code>git add . &amp;amp;&amp;amp; git commit -m 'add post' &amp;amp;&amp;amp; git push&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>Wait a few minutes for Travis CI, refresh the site, done. Only step 3 is creative—the rest is repetitive, and it assumes every machine has the repo and environment set up. Switching laptops meant jotting ideas down and waiting until I got back home. Not great.&lt;/p>
&lt;p>Time to apply the DRY principle and build a tool of my own.&lt;/p>
&lt;h2 id="architecture">
&lt;a class="heading-anchor-link" href="#architecture">Architecture&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="architecture"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I picked the stack I’m most comfortable with:&lt;/p>
&lt;ul>
&lt;li>Frontend: &lt;a href="https://angular.io/" target="_blank" rel="noopener">Angular&lt;/a>&lt;/li>
&lt;li>UI: &lt;a href="https://v4.bootcss.com/" target="_blank" rel="noopener">Bootstrap&lt;/a>&lt;/li>
&lt;li>Backend: &lt;a href="http://expressjs.com/" target="_blank" rel="noopener">Express.js&lt;/a>&lt;/li>
&lt;li>Serving static files: &lt;a href="https://www.nginx.com/" target="_blank" rel="noopener">Nginx&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="features">
&lt;a class="heading-anchor-link" href="#features">Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Authentication (login/logout)&lt;/li>
&lt;li>Post list &amp;amp; keyword search&lt;/li>
&lt;li>Dual-pane editor (write + preview)&lt;/li>
&lt;li>Publish Markdown + regenerate Hexo static files&lt;/li>
&lt;/ul>
&lt;h2 id="project-structure">
&lt;a class="heading-anchor-link" href="#project-structure">Project Structure&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-structure"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-18-170558.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Everything lives in one repo, but frontend and backend are still cleanly separated. That makes future steps—mobile app, storing Markdown in MongoDB, etc.—easier.&lt;/p>
&lt;h2 id="implementation-notes">
&lt;a class="heading-anchor-link" href="#implementation-notes">Implementation Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>There were plenty of details: rendering Markdown previews, autosave frequency, running shell commands via Node, serving generated pages with Nginx, and so on. Not covering every line of code here, but those were the interesting bits.&lt;/p>
&lt;h2 id="result">
&lt;a class="heading-anchor-link" href="#result">Result&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="result"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-18-172521.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Now I just open the web page, write, and publish. This very article was composed in the new editor. With responsive design, I can draft from mobile as well. It’s still rough around the edges, but iteration is the fun part.&lt;/p></description></item><item><title>Nginx Configuration for Domain-Only Web Access</title><link>https://en.1991421.cn/2018/06/02/nginxweb/</link><pubDate>Sat, 02 Jun 2018 12:35:56 +0800</pubDate><guid>https://en.1991421.cn/2018/06/02/nginxweb/</guid><description>&lt;blockquote>
&lt;p>After setting up DNS, we achieved domain access to the web site. But the server IP was still visible, so users could access the site via IP. This creates two problems:&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>If users access via IP and the server location changes, the IP changes and access fails.&lt;/li>
&lt;li>If someone maliciously points their own domain to our IP, they can also access the site.
So we should disable IP access and only allow specific domains.&lt;/li>
&lt;/ol>
&lt;h2 id="specific-configuration">
&lt;a class="heading-anchor-link" href="#specific-configuration">Specific configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="specific-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Below is my Nginx configuration for a site &lt;code>https://tool.alan.me&lt;/code>.&lt;/p>
&lt;h2 id="configure-a-specific-domain">
&lt;a class="heading-anchor-link" href="#configure-a-specific-domain">Configure a specific domain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configure-a-specific-domain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">listen&lt;/span> &lt;span class="mi">443&lt;/span> &lt;span class="n">ssl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server_name&lt;/span> &lt;span class="k">tool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">alanhe&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">me&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl&lt;/span> &lt;span class="n">on&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl_certificate&lt;/span> &lt;span class="s2">&amp;#34;/etc/nginx/ssl/fullchain.cer&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl_certificate_key&lt;/span> &lt;span class="s2">&amp;#34;/etc/nginx/ssl/tool.alanhe.me.key&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="add-default-service-configuration">
&lt;a class="heading-anchor-link" href="#add-default-service-configuration">Add default service configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-default-service-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">listen&lt;/span> &lt;span class="mi">443&lt;/span> &lt;span class="n">default_server&lt;/span> &lt;span class="n">ssl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server_name&lt;/span> &lt;span class="n">_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl&lt;/span> &lt;span class="n">on&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl_certificate&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nginx&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ssl&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">fullchain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cer&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl_certificate_key&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nginx&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ssl&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">tool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">alanhe&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">me&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">403&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After configuration, restart Nginx: &lt;code>nginx -s reload&lt;/code>.&lt;/p>
&lt;h2 id="effect">
&lt;a class="heading-anchor-link" href="#effect">Effect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="effect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When accessing via IP:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-06-02-045526.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>When accessing via the specified domain:
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-06-02-051256.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>Enabling HTTPS for a Website</title><link>https://en.1991421.cn/2018/05/30/https/</link><pubDate>Wed, 30 May 2018 22:33:12 +0800</pubDate><guid>https://en.1991421.cn/2018/05/30/https/</guid><description>&lt;blockquote>
&lt;p>Adding HTTPS has become the norm. Modern Chrome flags plaintext sites as &amp;ldquo;Not secure&amp;rdquo;. Beyond removing that warning, HTTPS genuinely protects data in transit. I used my own site as an example while documenting the configuration.&lt;/p>
&lt;/blockquote>
&lt;h2 id="lets-encrypt-certificates">
&lt;a class="heading-anchor-link" href="#lets-encrypt-certificates">Let&amp;rsquo;s Encrypt certificates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lets-encrypt-certificates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Alibaba Cloud used to offer Symantec certificates for free, but that ended. The good news is Let&amp;rsquo;s Encrypt delivers free SSL certificates. They only last 90 days, but we can automate renewal.&lt;/p>
&lt;h2 id="setup-steps">
&lt;a class="heading-anchor-link" href="#setup-steps">Setup steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="setup-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="install-acmesh">
&lt;a class="heading-anchor-link" href="#install-acmesh">Install acme.sh&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-acmesh"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl https://get.acme.sh &lt;span class="p">|&lt;/span> sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="nginx-config-expose-the-challenge-path">
&lt;a class="heading-anchor-link" href="#nginx-config-expose-the-challenge-path">NGINX config: expose the challenge path&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nginx-config-expose-the-challenge-path"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When issuing a certificate, the CA checks files under &lt;code>.well-known/acme-challenge/&lt;/code>. We need that path accessible.&lt;/p>
&lt;p>Example for an NGINX container:&lt;/p>
&lt;ul>
&lt;li>After validation the folder is cleaned up, so you won&amp;rsquo;t see files sitting there. Keep the path accessible anyway because renewals need it.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">location&lt;/span> &lt;span class="o">^~&lt;/span> &lt;span class="o">/.&lt;/span>&lt;span class="n">well&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">known&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">acme&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">challenge&lt;/span>&lt;span class="o">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">alias&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">www&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">toolkit&lt;/span>&lt;span class="o">/.&lt;/span>&lt;span class="n">well&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">known&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">acme&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">challenge&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">try_files&lt;/span> &lt;span class="o">$&lt;/span>&lt;span class="n">uri&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="issue-the-certificate">
&lt;a class="heading-anchor-link" href="#issue-the-certificate">Issue the certificate&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="issue-the-certificate"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">acme&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sh&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">issue&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="k">tool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">alanhe&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">me&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">webroot&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">www&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">toolkit&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="copy-the-certs-to-nginx">
&lt;a class="heading-anchor-link" href="#copy-the-certs-to-nginx">Copy the certs to NGINX&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="copy-the-certs-to-nginx"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;code>/etc/nginx/ssl&lt;/code> may not exist; create it with &lt;code>mkdir /etc/nginx/ssl&lt;/code> if needed.&lt;/li>
&lt;li>The following command installs the certs automatically—no manual copying required.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">acme&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sh&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">installcert&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="k">tool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">alanhe&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">me&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nginx&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ssl&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">tool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">alanhe&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">me&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">key&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">fullchain&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">file&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nginx&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ssl&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">fullchain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cer&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">reloadcmd&lt;/span> &lt;span class="s2">&amp;#34;service nginx force-reload&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the command completes successfully, wire the certificate into your NGINX config.&lt;/p>
&lt;h3 id="nginx-https-config">
&lt;a class="heading-anchor-link" href="#nginx-https-config">NGINX HTTPS config&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nginx-https-config"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">listen&lt;/span> &lt;span class="mi">443&lt;/span> &lt;span class="n">ssl&lt;/span> &lt;span class="n">http2&lt;/span> &lt;span class="n">default_server&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">listen&lt;/span> &lt;span class="p">[::]:&lt;/span>&lt;span class="mi">443&lt;/span> &lt;span class="n">ssl&lt;/span> &lt;span class="n">http2&lt;/span> &lt;span class="n">default_server&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server_name&lt;/span> &lt;span class="k">tool&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">alan&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">me&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl&lt;/span> &lt;span class="n">on&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl_certificate&lt;/span> &lt;span class="s2">&amp;#34;/etc/nginx/ssl/fullchain.cer&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ssl_certificate_key&lt;/span> &lt;span class="s2">&amp;#34;/etc/nginx/ssl/tool.alanhe.me.key&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run &lt;code>nginx -t&lt;/code>; if it reports &lt;code>OK&lt;/code>, reload NGINX and visit &lt;code>https://tool.alanhe.me&lt;/code>. You should see the green lock.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-31-030134.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="certificate-renewals">
&lt;a class="heading-anchor-link" href="#certificate-renewals">Certificate renewals&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="certificate-renewals"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Installing acme.sh adds a cron job that checks and renews certificates regularly. No manual work needed. Confirm via &lt;code>crontab -e&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="m">16&lt;/span> &lt;span class="m">0&lt;/span> * * * &lt;span class="s2">&amp;#34;/root/.acme.sh&amp;#34;&lt;/span>/acme.sh --cron --home &lt;span class="s2">&amp;#34;/root/.acme.sh&amp;#34;&lt;/span> &amp;gt; /dev/null
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>That’s the complete flow. To simplify future deployments (especially when moving servers), I later containerized the setup. Pull the Docker config, run it, and everything is ready. My blog deployment files are &lt;a href="https://github.com/alanhe421/alanhe421.github.io/tree/master/deploy" target="_blank" rel="noopener">here&lt;/a> for reference.&lt;/p>
&lt;h2 id="https-security-briefly">
&lt;a class="heading-anchor-link" href="#https-security-briefly">HTTPS security, briefly&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="https-security-briefly"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>HTTPS is more secure because it encrypts data. TCP requires a three-way handshake; HTTP over TLS rides on a TLS handshake (adding four more steps: exchanging and confirming secrets). Diagram from Li Bing’s &lt;em>The Working Principles and Practice of the Browser&lt;/em>:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2021/2021-03-11-151647.jpeg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>HTTPS is not bulletproof. Certain attacks (e.g., man-in-the-middle) can still succeed under the right conditions, so keep an eye on your overall security posture.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E" target="_blank" rel="noopener">acme.sh&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://imququ.com/post/letsencrypt-certificate.html" target="_blank" rel="noopener">Let&amp;rsquo;s Encrypt — free HTTPS certificates&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://czjake.com/blog/article/https-certificate-letsencrypt" target="_blank" rel="noopener">Using Let&amp;rsquo;s Encrypt certificates&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Nginx User IP Passthrough</title><link>https://en.1991421.cn/2018/05/27/nginxip/</link><pubDate>Sun, 27 May 2018 10:46:01 +0800</pubDate><guid>https://en.1991421.cn/2018/05/27/nginxip/</guid><description>&lt;blockquote>
&lt;p>Recently developing WEB, involved IP login, backend needs to get user access IP. Since backend is ExpressJS, req.ip can get it, but after actual deployment, found req.ip is always &lt;code>127.0.0.1&lt;/code>
Thought about WEB deployment using Nginx back then, suddenly understood this point, this is Nginx&amp;rsquo;s fault. Because Nginx acts as reverse proxy, in other words for our WEB backend, the requester is Nginx, so IP always being 127.0.0.1 makes sense.&lt;/p>
&lt;/blockquote>
&lt;h2 id="ip-passthrough">
&lt;a class="heading-anchor-link" href="#ip-passthrough">IP Passthrough&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ip-passthrough"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Googled it, solution to this problem is doing Nginx IP passthrough, configuration as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">location / {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> proxy_pass http://127.0.0.1:3001/;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> proxy_set_header Host $host;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> proxy_set_header X-Real-IP $remote_addr;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After modification OK, execute following commands&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Test if configuration is correct&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">nginx&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">t&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Reload configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">nginx&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">s&lt;/span> &lt;span class="n">reload&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>[Translation] Differences Between Angular 1 and 2</title><link>https://en.1991421.cn/2018/05/22/angular1-2/</link><pubDate>Tue, 22 May 2018 17:55:11 +0800</pubDate><guid>https://en.1991421.cn/2018/05/22/angular1-2/</guid><description>&lt;p>Original article: &lt;a href="http://www.learnangularjs.net/differencebetweenangularjs1vsangularjs2.php" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;h2 id="preface">
&lt;a class="heading-anchor-link" href="#preface">Preface&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="preface"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Angular is already at v6.0.3, so why revisit the jump from 1.x to 2? Because versions 2/4/5/6 iterate on the same architectural foundations. Modules, components, pipes, DI, directives, and routing still look like Angular 2. Understanding the 1.x → 2 transition explains everything that followed.&lt;/p>
&lt;h2 id="highlights-of-the-shift">
&lt;a class="heading-anchor-link" href="#highlights-of-the-shift">Highlights of the shift&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="highlights-of-the-shift"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Angular 2 isn’t a minor upgrade—it’s a rewrite. Here’s where it diverges from Angular 1.x.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>Angular 2 is a complete redesign rather than a patch on top of 1.x.&lt;/li>
&lt;li>Controllers (a 1.x concept) disappear; Angular 2 embraces web-standard components.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-22-093030.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>&lt;code>$scope&lt;/code> is gone. Angular 2 relies on Zone.js to help with change detection.&lt;/li>
&lt;li>Mobile support becomes a first-class goal in Angular 2; 1.x had limited mobile stories.&lt;/li>
&lt;li>TypeScript is the recommended language for Angular 2—bringing static typing, decorators, and ES6+ features. Angular 1.x was plain JavaScript.&lt;/li>
&lt;li>Dependency injection becomes hierarchical, and change detection adopts a unidirectional tree approach for better performance.&lt;/li>
&lt;li>DI is expressed through constructor parameters instead of inline &lt;code>$inject&lt;/code> arrays.&lt;/li>
&lt;li>Template local variables use the &lt;code>#variable&lt;/code> syntax.&lt;/li>
&lt;li>Structural directives switch from &lt;code>ng-repeat&lt;/code> to &lt;code>*ngFor&lt;/code>.&lt;/li>
&lt;li>Directive names move from dash-case (&lt;code>ng-class&lt;/code>) to camelCase (&lt;code>ngClass&lt;/code>, &lt;code>ngModel&lt;/code>).&lt;/li>
&lt;li>Bootstrapping is unified—no more &lt;code>ng-app&lt;/code> attribute. You bootstrap via code.&lt;/li>
&lt;li>Routing is built around decorators instead of &lt;code>$routeProvider&lt;/code>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ts" data-lang="ts">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@Routes&lt;/span>&lt;span class="p">([&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">component&lt;/span>: &lt;span class="kt">HomeComponent&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;/contact&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">component&lt;/span>: &lt;span class="kt">ContactComponent&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>And templates use &lt;code>routerLink&lt;/code> rather than &lt;code>ng-href&lt;/code>:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="err">[&lt;/span>&lt;span class="na">routerLink&lt;/span>&lt;span class="err">]=&amp;#34;[&amp;#39;/&amp;#39;]&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Home&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="err">[&lt;/span>&lt;span class="na">routerLink&lt;/span>&lt;span class="err">]=&amp;#34;[&amp;#39;/&lt;/span>&lt;span class="na">contact&lt;/span>&lt;span class="err">&amp;#39;]&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Contact&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>Helpers like &lt;code>ng-href&lt;/code>, &lt;code>ng-src&lt;/code>, and &lt;code>ng-hide&lt;/code> are unnecessary—use native &lt;code>href&lt;/code>, &lt;code>src&lt;/code>, and &lt;code>hidden&lt;/code>.&lt;/li>
&lt;li>One-way binding (&lt;code>ng-bind&lt;/code>) becomes &lt;code>[property]&lt;/code> binding.&lt;/li>
&lt;li>Two-way binding (&lt;code>ng-model&lt;/code>) becomes &lt;code>[(ngModel)]&lt;/code>.&lt;/li>
&lt;li>Angular 1.x was easy to start—drop in a script tag. Angular 2 depends on Zone.js, SystemJS, shim.js, Reflect Metadata, plus Node.js and TypeScript tooling. Miss any of those and the app won’t bootstrap.&lt;/li>
&lt;/ol>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-23-065813.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Hopefully this clarifies the biggest deltas between Angular 1.x and Angular 2.&lt;/p></description></item><item><title>Upgrading Angular 5 to 6</title><link>https://en.1991421.cn/2018/05/05/682d106/</link><pubDate>Sat, 05 May 2018 23:33:16 +0800</pubDate><guid>https://en.1991421.cn/2018/05/05/682d106/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-06-022853.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>Angular 6.0.0 has been officially released, exciting! Of course, this is already a month overdue, and many open source projects seem to be like this, such as Ionic v4, so, understandable.
I&amp;rsquo;ve learned about v6 before, and it&amp;rsquo;s said that size, performance, and functionality have all been improved, so I quickly followed up and recorded it roughly here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="upgrading-the-project">
&lt;a class="heading-anchor-link" href="#upgrading-the-project">Upgrading the Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="upgrading-the-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For specific upgrades, it&amp;rsquo;s recommended to get the guide from the official website: &lt;a href="https://update.angular.io/" target="_blank" rel="noopener">https://update.angular.io/&lt;/a>&lt;/p>
&lt;p>Below is a comparison of package version changes before and after upgrading my project:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-05-154225.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="important-notes">
&lt;a class="heading-anchor-link" href="#important-notes">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The actual upgrade is not just changing a few package versions. Note that because RxJS 6 has significant changes and deprecated some operators, it&amp;rsquo;s easier to use the official upgrade tool for RxJS.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ npm install -g rxjs-tslint
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ rxjs-5-to-6-migrate -p src/tsconfig.app.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>However, even after successfully executing the above, there are still some parts of the code that we need to manually modify ourselves, otherwise errors will occur, such as this operator modification here.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Property &amp;#39;map&amp;#39; does not exist on type &amp;#39;Observable&amp;lt;Response&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-26-052755.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-26-061753.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After the successful upgrade, I compared the build speed, bundle size, and web performance. The results are as follows:&lt;/p>
&lt;h2 id="size-and-build-speed">
&lt;a class="heading-anchor-link" href="#size-and-build-speed">Size and Build Speed&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="size-and-build-speed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-05-153831.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="performance">
&lt;a class="heading-anchor-link" href="#performance">Performance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="performance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-05-154001.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The comparison shows that the size is indeed smaller than before, but not by much - the size reduction is within 10%. Of course, different projects with different dependencies will vary. In terms of build speed, mine seems to have gotten slower&amp;hellip; Performance-wise, you can see slight improvements in loading speed and script execution speed.
Overall, none of these are very noticeable&amp;hellip;&lt;/p>
&lt;p>Of course, all of this is based on not having enabled &lt;code>ivy&lt;/code> yet, which is not enabled by default. I&amp;rsquo;m looking forward to Angular and related third-party components developing for a while longer before tackling this. I recommend not playing with ivy now - it&amp;rsquo;s still early.&lt;/p></description></item><item><title>Caching Static Assets for Frontend</title><link>https://en.1991421.cn/2018/04/20/frontend-static-asset-caching/</link><pubDate>Fri, 20 Apr 2018 13:04:56 +0800</pubDate><guid>https://en.1991421.cn/2018/04/20/frontend-static-asset-caching/</guid><description>&lt;blockquote>
&lt;p>For static resources that don&amp;rsquo;t change in web pages, we hope the browser won&amp;rsquo;t request the backend again once it gets them, until these resources are updated. The essence of this requirement is that we need to add version management to frontend resources.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-30-121129.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="benefits-of-frontend-caching">
&lt;a class="heading-anchor-link" href="#benefits-of-frontend-caching">Benefits of frontend caching&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="benefits-of-frontend-caching"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Reduce the number of client requests for resources, thereby improving page load speed&lt;/li>
&lt;li>Reduce server load pressure&lt;/li>
&lt;/ol>
&lt;p>It seems everyone benefits. So how to implement frontend resource version management for caching? There are actually many methods.&lt;/p>
&lt;h2 id="cache-controlexpires-last-modifiedetag">
&lt;a class="heading-anchor-link" href="#cache-controlexpires-last-modifiedetag">Cache-Control/Expires, Last-Modified/ETag&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cache-controlexpires-last-modifiedetag"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After setting Cache-Control/Expires, the browser won&amp;rsquo;t send requests for resources during normal page visits and will directly read from the browser cache. If the user clicks the &amp;ldquo;Refresh&amp;rdquo; button or the cache time expires, the browser will send a request and use Last-Modified/ETag to determine if the content has been updated. If the content hasn&amp;rsquo;t changed, the server returns a &lt;a href="https://httpstatuses.com/304" target="_blank" rel="noopener">304&lt;/a> status.&lt;/p>
&lt;h3 id="problem">
&lt;a class="heading-anchor-link" href="#problem">Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After a new release, when accessing the page, resources will still be cached. Users need to refresh or re-visit to request the backend for ETag verification.&lt;/p>
&lt;h2 id="version-numbers">
&lt;a class="heading-anchor-link" href="#version-numbers">Version numbers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="version-numbers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Adding version numbers makes previous frontend resources invalid with each release, thus requesting new resources. However, this brings problems.&lt;/p>
&lt;h3 id="problems">
&lt;a class="heading-anchor-link" href="#problems">Problems&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problems"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Version numbers must be applied to a batch of resources, such as all frontend resources (JS, CSS, IMG) of a project. For example, if an emergency release only changes a CSS style, the release will invalidate all resource caches.&lt;/li>
&lt;li>Version numbers need to be maintained. You have to remember to update the version number for each new release (including minor patches), and the maintenance cost is actually not low.&lt;/li>
&lt;/ol>
&lt;h2 id="hash-fingerprints">
&lt;a class="heading-anchor-link" href="#hash-fingerprints">Hash fingerprints&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="hash-fingerprints"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the content changes, the fingerprint changes; otherwise, it remains the same. This ensures that only the cache of the changed content file is invalidated.&lt;/p>
&lt;h3 id="pros">
&lt;a class="heading-anchor-link" href="#pros">Pros&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pros"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>When a specific file is updated, it won&amp;rsquo;t invalidate other files&amp;rsquo; caches like version number management does&lt;/li>
&lt;li>Non-overwriting updates: new versions have different names from old versions, so there&amp;rsquo;s no conflict. This prevents the problem of new HTML using old JS.&lt;/li>
&lt;/ol>
&lt;h3 id="cons">
&lt;a class="heading-anchor-link" href="#cons">Cons&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cons"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To add hash fingerprints, you need to add processing steps during the build phase, which will increase the build time. But this overhead shouldn&amp;rsquo;t be a big problem for build servers or even personal development machines.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above methods represent the development history of frontend resource management in web development. Currently, the relatively optimal solution is hash fingerprinting. So let&amp;rsquo;s implement it quickly, allowing us to release anytime, 7x24.&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/25953524" target="_blank" rel="noopener">Summary of Browser Cache Mechanisms&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.jianshu.com/p/fbc519d43924" target="_blank" rel="noopener">Frontend Static Resource Version Management Based on Gulp&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://juejin.im/post/5a098b5bf265da431a42b227" target="_blank" rel="noopener">Web Static Resource Caching and Optimization&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/island205/island205.github.com/issues/12" target="_blank" rel="noopener">Talking About How to Organize Online Static Resources&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://imweb.io/topic/5795dcb6fb312541492eda8c" target="_blank" rel="noopener">HTTP Cache Control Summary&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Mobile Frontend Debugging</title><link>https://en.1991421.cn/2018/04/14/mobile-frontend-debugging/</link><pubDate>Sat, 14 Apr 2018 12:03:34 +0800</pubDate><guid>https://en.1991421.cn/2018/04/14/mobile-frontend-debugging/</guid><description>&lt;blockquote>
&lt;p>Web development today isn’t just for desktops—mobile matters just as much. Real-world launches surface plenty of issues, so reliable debugging techniques are essential.&lt;/p>
&lt;/blockquote>
&lt;p>How do you debug mobile frontends? After digging through documentation and trial-and-error in projects, here’s the workflow I rely on.&lt;/p>
&lt;h2 id="responsive-testing">
&lt;a class="heading-anchor-link" href="#responsive-testing">Responsive testing&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="responsive-testing"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sites with modest UI components—personal blogs, corporate marketing pages—should already be responsive.&lt;/p>
&lt;h3 id="drag-to-resize">
&lt;a class="heading-anchor-link" href="#drag-to-resize">Drag to resize&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="drag-to-resize"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Testing responsiveness is simple: open Chrome DevTools and drag to resize the viewport.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-15-%E5%93%8D%E5%BA%94%E5%BC%8F%E6%B5%8B%E8%AF%95.gif"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="device-emulation">
&lt;a class="heading-anchor-link" href="#device-emulation">Device emulation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="device-emulation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Need to test a specific screen size without the device? Chrome’s device emulation lets you select popular phones or define custom dimensions and device pixel ratios.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-15-132447.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>It also supports screenshots and other utilities—a genuine power tool.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-22-134335.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="android-debugging">
&lt;a class="heading-anchor-link" href="#android-debugging">Android debugging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="android-debugging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="android-emulator">
&lt;a class="heading-anchor-link" href="#android-emulator">Android Emulator&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="android-emulator"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The Android Studio emulator is sufficient for most scenarios.&lt;/p>
&lt;h3 id="android-on-device">
&lt;a class="heading-anchor-link" href="#android-on-device">Android on device&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="android-on-device"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Connect the phone with a USB cable and enable &lt;code>USB debugging&lt;/code> under Developer Options.&lt;/li>
&lt;li>In desktop Chrome, open &lt;code>chrome://inspect&lt;/code> to locate the device.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-30-080015.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;li>Click &lt;em>inspect&lt;/em> to launch DevTools for the target page.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-30-080132.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;/ol>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>The browser on the device must be &lt;code>Chrome&lt;/code>.&lt;/li>
&lt;li>The initial load may require a VPN/proxy if DevTools tries to fetch remote resources.&lt;/li>
&lt;li>The same setup works for WebView-based apps (e.g., Ionic) because they ultimately render web content.&lt;/li>
&lt;/ol>
&lt;h2 id="ios-debugging">
&lt;a class="heading-anchor-link" href="#ios-debugging">iOS debugging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ios-debugging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Apple hardware works seamlessly together, and testing follows the same pattern. To debug mobile Safari, first enable the Develop menu in desktop Safari preferences—it’s hidden by default.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-15-134103.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="ios-simulator">
&lt;a class="heading-anchor-link" href="#ios-simulator">iOS Simulator&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ios-simulator"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Pick a device and OS version in the simulator, run the app, then in desktop Safari go to Develop → iPhone Simulator. Select the page to open the Safari DevTools panel.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-30-081030.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-15-134740.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="ios-on-device">
&lt;a class="heading-anchor-link" href="#ios-on-device">iOS on device&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ios-on-device"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Simulators are great, but real devices provide the most accurate experience. Debugging hardware mirrors the simulator workflow.&lt;/p>
&lt;p>Connect the phone via cable and enable the Web Inspector in Safari: &lt;code>Settings → Safari → Advanced → Web Inspector&lt;/code>.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018/2018-06-25-378987AD38B14686626F8EA5F5D09095.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-30-080900.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="wechat-webview">
&lt;a class="heading-anchor-link" href="#wechat-webview">WeChat WebView&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wechat-webview"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>In 2016, WeChat (including the international edition) had 889 million monthly active users. With a user base that large, many mobile pages are WeChat-first, so debugging inside WeChat is critical. Use the official &lt;a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html" target="_blank" rel="noopener">WeChat Developer Tools&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-15-133415.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-22-135106.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>On Android, pair the Chrome app with desktop Chrome. On iOS, pair Safari with desktop Safari. Both ecosystems offer mature debugging options. If an iOS user browses with something like QQ Browser, these methods won’t apply—you’ll need alternate tooling (see Reference 1).&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://yujiangshui.com/multidevice-frontend-debug/#%E4%BD%BF%E7%94%A8-iOS-Simulator-%E8%B0%83%E8%AF%95%E5%BC%80%E5%8F%91" target="_blank" rel="noopener">Mobile Frontend Debugging&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/5794984/how-to-debug-web-sites-on-mobile-devices" target="_blank" rel="noopener">How to debug web sites on mobile devices?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tidycustoms.net/blog/debugging-website-on-mobile-devices" target="_blank" rel="noopener">Inspecting and debugging mobile websites&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>oh my zsh Configuration File Not Loading</title><link>https://en.1991421.cn/2018/04/07/oh-my-zsh/</link><pubDate>Sat, 07 Apr 2018 22:19:44 +0800</pubDate><guid>https://en.1991421.cn/2018/04/07/oh-my-zsh/</guid><description>&lt;blockquote>
&lt;p>zsh is an excellent shell, but I encountered an issue where I had to manually execute &lt;code>source ~/.zshrc&lt;/code> every time. When I opened a new terminal tab or restarted my machine, I had to run the source command again, otherwise it would report command not found errors.
I deduced that the oh my zsh configuration file was not loading automatically.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-07-142912.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
If this had to be done every time, it would be unacceptable. So there must be a solution - please read on.&lt;/p>
&lt;/blockquote>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Terminal &amp;ndash;&amp;gt; Preferences &amp;ndash;&amp;gt; General &amp;ndash;&amp;gt; Shells open with&lt;/li>
&lt;li>Set Shells open with to Command (complete path) &lt;code>/bin/zsh&lt;/code>&lt;/li>
&lt;li>Set New windows open with to &lt;code>Same Profile&lt;/code>&lt;/li>
&lt;li>Set New tabs open with to &lt;code>Same Profile&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-04-07-142448.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="reference-links">
&lt;a class="heading-anchor-link" href="#reference-links">Reference Links&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-links"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/15682456/oh-my-zsh-config-file-not-loading" target="_blank" rel="noopener">https://stackoverflow.com/questions/15682456/oh-my-zsh-config-file-not-loading&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Jenkins Continuous Integration - WAR Package Deployment</title><link>https://en.1991421.cn/2018/03/25/jenkins-war/</link><pubDate>Sun, 25 Mar 2018 13:50:15 +0800</pubDate><guid>https://en.1991421.cn/2018/03/25/jenkins-war/</guid><description>&lt;blockquote>
&lt;p>For JAVA web development, if you choose Tomcat container, it&amp;rsquo;s generally WAR package deployment&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-03-25-061434.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>For Jenkins WAR package deployment, there are two approaches: one is installing the &lt;a href="https://plugins.jenkins.io/deploy" target="_blank" rel="noopener">&lt;code>Deploy to container&lt;/code>&lt;/a> plugin, the other is writing your own shell script solution. I chose approach 2 here for two reasons:&lt;/p>
&lt;ol>
&lt;li>The Jenkins plugin itself has low activity, meaning if there are any issues, you&amp;rsquo;re stuck. Currently it only supports up to Tomcat 7.x&lt;/li>
&lt;li>The plugin uses Tomcat-manager module for direct deployment, which I believe has poor performance&lt;/li>
&lt;/ol>
&lt;p>So, without further ado, let&amp;rsquo;s get started!&lt;/p>
&lt;h2 id="war-package-transfer-to-target-server">
&lt;a class="heading-anchor-link" href="#war-package-transfer-to-target-server">WAR Package Transfer to Target Server&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="war-package-transfer-to-target-server"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>File transfer uses &lt;code>Publish Over SSH&lt;/code>, here I&amp;rsquo;m transferring to &lt;code>/opt/deploy/war&lt;/code>&lt;/p>
&lt;h2 id="writing-deployment-script">
&lt;a class="heading-anchor-link" href="#writing-deployment-script">Writing Deployment Script&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="writing-deployment-script"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;code>vi /opt/deploy/deploy.sh&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#!/bin/bash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">source&lt;/span> /etc/profile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">cd&lt;/span> /tmp/deploy/war
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> jar -xvf *.war
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rm -f *.war
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="se">\c&lt;/span>p -rf * /usr/local/tomcat/webapps/ROOT/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> rm -rf *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> systemctl restart tomcat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note:&lt;/em> I&amp;rsquo;m using Centos 7.x here, so service restart uses &lt;code>systemctl&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>chmod +x deploy.sh&lt;/code>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="jenkins-build-task-configuration">
&lt;a class="heading-anchor-link" href="#jenkins-build-task-configuration">Jenkins Build Task Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="jenkins-build-task-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-05-25-035825.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Shell scripts implement the specific execution details of the build, while Jenkins provides hooks and mechanism support. This combination solves deployment and provides great flexibility. This way, we only need to focus on programming, while the tedious build and deployment work is handed over to robots, saving us a lot of time. This is the purpose of DevOps.&lt;/p></description></item><item><title>HTTP Referer in HTTP Requests</title><link>https://en.1991421.cn/2018/03/19/httpreferer/</link><pubDate>Mon, 19 Mar 2018 23:18:37 +0800</pubDate><guid>https://en.1991421.cn/2018/03/19/httpreferer/</guid><description>&lt;blockquote>
&lt;p>Previously, when integrating our system with other sites for login authentication, we used the Referer header. Here&amp;rsquo;s a summary of what I learned.&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-is-the-referer-header">
&lt;a class="heading-anchor-link" href="#what-is-the-referer-header">What is the Referer Header&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-the-referer-header"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>HTTP Referer is a field in the HTTP header that tells the server which page the request came from, allowing the server to perform specific processing based on this information.&lt;/p>
&lt;p>For example, if my system supports links coming from website A to enable passwordless login, then I need to check the Referer header.&lt;/p>
&lt;h2 id="does-windowopen-carry-referer-in-link-requests">
&lt;a class="heading-anchor-link" href="#does-windowopen-carry-referer-in-link-requests">Does window.open() Carry Referer in Link Requests?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="does-windowopen-carry-referer-in-link-requests"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Testing shows that IE loses the Referer header, while Edge preserves it, and Chrome definitely preserves it. Therefore, if your site requires the Referer header to be present, it&amp;rsquo;s recommended to use alternative methods such as location changes or direct hyperlinks.&lt;/p>
&lt;h2 id="related-resources">
&lt;a class="heading-anchor-link" href="#related-resources">Related Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/HTTP_referer" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/HTTP_referer&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://75team.com/post/everything-you-could-ever-want-to-know-and-more-about-controlling-the-referer-header-fastmail-blog.html" target="_blank" rel="noopener">https://75team.com/post/everything-you-could-ever-want-to-know-and-more-about-controlling-the-referer-header-fastmail-blog.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The HTTP Referer header is a useful but sometimes unreliable mechanism for tracking request origins across different browsers. While modern browsers generally support it well, legacy browsers like Internet Explorer may drop the header in certain scenarios. For critical authentication or security use cases, consider implementing additional verification mechanisms alongside Referer checks.&lt;/p></description></item><item><title>Linux File System</title><link>https://en.1991421.cn/2018/03/18/linux/</link><pubDate>Sun, 18 Mar 2018 23:02:28 +0800</pubDate><guid>https://en.1991421.cn/2018/03/18/linux/</guid><description>&lt;blockquote>
&lt;p>I was driving a company&amp;rsquo;s CI/CD and used Linux more often. I had used it before, but since my main role was system development, my fundamentals were not solid. When installing Tomcat, JDK, FTP, etc., I often wondered where software should be installed or which path is appropriate. This wasted time. So I decided to dig into it to save time.&lt;/p>
&lt;/blockquote>
&lt;p>This article is based on research across multiple sources.&lt;/p>
&lt;p>For the Linux file system, you should read the &lt;a href="http://www.pathname.com/fhs/" target="_blank" rel="noopener">Filesystem Hierarchy Standard&lt;/a>, which explains the origin of these names.&lt;/p>
&lt;ul>
&lt;li>/bin - binaries&lt;/li>
&lt;li>/boot - boot files&lt;/li>
&lt;li>/dev - device files&lt;/li>
&lt;li>/tmp - temporary files, writable by everyone&lt;/li>
&lt;li>/etc - inherited from early Unix, for configuration files&lt;/li>
&lt;li>/home - home directories&lt;/li>
&lt;li>/lib - libraries&lt;/li>
&lt;li>/media - mount points for removable media&lt;/li>
&lt;li>/mnt - mounted file systems&lt;/li>
&lt;li>/opt - optional add-on software&lt;/li>
&lt;li>/run - runtime variable data&lt;/li>
&lt;li>/sbin - usually for root&lt;/li>
&lt;li>/usr - inherited from Unix, stands for user, shared among users&lt;/li>
&lt;li>/var - inherited from Unix, stands for &lt;code>variable&lt;/code>&lt;/li>
&lt;li>/srv - stands for &lt;code>serve&lt;/code>, used for hosting static files, &lt;code>/srv/http&lt;/code> for static sites, &lt;code>/srv/ftp&lt;/code> for FTP&lt;/li>
&lt;/ul>
&lt;h2 id="opt-vs-usrlocal">
&lt;a class="heading-anchor-link" href="#opt-vs-usrlocal">/opt vs /usr/local&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="opt-vs-usrlocal"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>/usr/local is typically for things installed into /usr or overrides of /usr. /opt keeps everything in one folder or special cases.&lt;/p>
&lt;/blockquote>
&lt;h2 id="when-tmp-is-cleared">
&lt;a class="heading-anchor-link" href="#when-tmp-is-cleared">When /tmp is cleared&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="when-tmp-is-cleared"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>If you put files in /tmp, they may disappear after a while because CentOS has a scheduled task &lt;code>tmpwatch&lt;/code> that cleans old files (over 10 days) in /tmp. Other systems like Debian clear /tmp on startup.&lt;/p>
&lt;/blockquote>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://serverfault.com/questions/24523/meaning-of-directories-on-unix-and-unix-like-systems" target="_blank" rel="noopener">https://serverfault.com/questions/24523/meaning-of-directories-on-unix-and-unix-like-systems&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>next in Express.js Explained (Simple Guide)</title><link>https://en.1991421.cn/2018/03/04/expressjsnext/</link><pubDate>Sun, 04 Mar 2018 15:41:05 +0800</pubDate><guid>https://en.1991421.cn/2018/03/04/expressjsnext/</guid><description>&lt;blockquote>
&lt;p>For lightweight project development, teams often use Express.js as the backend because it&amp;rsquo;s simple enough, easy to get started with, and naturally suitable for RESTful APIs. During usage, I&amp;rsquo;ve gradually deepened my understanding of Express and found that the official documentation has very little explanation about next, so I&amp;rsquo;ll organize this to deepen understanding.&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-role-of-next">
&lt;a class="heading-anchor-link" href="#the-role-of-next">The Role of Next&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-role-of-next"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>next is responsible for passing control to the next middleware function.&lt;/p>
&lt;h2 id="next-usage-scenarios-and-why-we-need-next">
&lt;a class="heading-anchor-link" href="#next-usage-scenarios-and-why-we-need-next">Next Usage Scenarios and Why We Need Next&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="next-usage-scenarios-and-why-we-need-next"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When handling route requests, we might need the next middleware to process, so we should use the next function. For example, exception handling or branched request processing.&lt;/p>
&lt;h2 id="code-examples">
&lt;a class="heading-anchor-link" href="#code-examples">Code Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code-examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="example-1">
&lt;a class="heading-anchor-link" href="#example-1">Example 1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">router.get(&amp;#34;/hello&amp;#34;, function (req, res, next) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> res.write(&amp;#34;hello&amp;#34;);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> next();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">});
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">router.get(&amp;#34;/hello&amp;#34;, function (req, res) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> res.write(&amp;#34; world&amp;#34;);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> res.end();
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">});
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After execution, result screenshot&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-03-04-083842.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
When we haven&amp;rsquo;t ended the request response, we can use next to continue processing.&lt;/p>
&lt;h3 id="example-2">
&lt;a class="heading-anchor-link" href="#example-2">Example 2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">router.get(&amp;#34;/hello&amp;#34;, function (req, res, next) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> if (req.query.name) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return res.json(&amp;#34;hello&amp;#34;);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> else {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> next(new Error(&amp;#34;No name provided&amp;#34;));
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">});
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">router.use(function (err, req, res, next) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> res.json({
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> message: err.message,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> error: {}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> });
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">});
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Execute request &lt;code>http://localhost:3001/api/hello&lt;/code>
Result screenshot
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-03-04-092302.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Exception handling is actually one use case of next.&lt;/p>
&lt;p>&lt;em>Note:&lt;/em> Since exception handling is done after the route request here, the exception middleware handling comes after the route request handling.&lt;/p>
&lt;h2 id="related-documentation">
&lt;a class="heading-anchor-link" href="#related-documentation">Related Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://github.com/expressjs/express" target="_blank" rel="noopener">Express.js Source Code&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cnodejs.org/topic/5757e80a8316c7cb1ad35bab" target="_blank" rel="noopener">Some Understanding of the next Function in Express&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://qnimate.com/express-js-middleware-tutorial/" target="_blank" rel="noopener">Express.js Middleware Tutorial&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Install Maven in Zsh on Mac</title><link>https://en.1991421.cn/2018/03/04/maczshmaven/</link><pubDate>Sun, 04 Mar 2018 15:23:57 +0800</pubDate><guid>https://en.1991421.cn/2018/03/04/maczshmaven/</guid><description>&lt;blockquote>
&lt;p>Maven is a build tool for Java development. This post shows how to install it in a Zsh environment.&lt;/p>
&lt;/blockquote>
&lt;h2 id="download-maven">
&lt;a class="heading-anchor-link" href="#download-maven">Download Maven&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download-maven"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-03-04-072623.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Unzip it and place it in a directory such as &lt;code>/Library/apache-maven-3.5.2&lt;/code>.&lt;/p>
&lt;h2 id="open-terminal-eg-iterm2-and-edit-zshrc">
&lt;a class="heading-anchor-link" href="#open-terminal-eg-iterm2-and-edit-zshrc">Open terminal (e.g. iTerm2) and edit zshrc&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-terminal-eg-iterm2-and-edit-zshrc"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ vi ~/.zshrc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Append the following at the end of the file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="k">export&lt;/span> &lt;span class="n">M2_HOME&lt;/span>&lt;span class="o">=/&lt;/span>&lt;span class="n">Library&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">apache&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">maven&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mf">3.5&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">export&lt;/span> &lt;span class="n">M2&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="n">M2_HOME&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">export&lt;/span> &lt;span class="n">PATH&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="n">M2&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">PATH&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Exit the editor, close the terminal, and restart it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-03-04-072933.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>jiathis HTTPS Unsupported Solution</title><link>https://en.1991421.cn/2018/02/25/jiathishttps/</link><pubDate>Sun, 25 Feb 2018 13:13:47 +0800</pubDate><guid>https://en.1991421.cn/2018/02/25/jiathishttps/</guid><description>&lt;blockquote>
&lt;p>Third-party social sharing plugins commonly used are baidushare, jiathis, but these don&amp;rsquo;t support HTTPS. If used on HTTPS sites, they will report errors.
How to solve? Actually can only modify source code to solve, or use reverse proxy.&lt;/p>
&lt;/blockquote>
&lt;p>Baidu share source code modification solution see &lt;a href="https://github.com/hrwhisper/baiduShare" target="_blank" rel="noopener">here&lt;/a>&lt;/p>
&lt;p>jiathis JS source code is already obfuscated, so cannot modify source code to solve. If still want to use, can directly download related code and deploy on your own WEB, but resource paths cannot change&lt;/p>
&lt;p>Structure and resources as follows:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-25-063251.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>jia.js file can be placed freely, but related css and img resources don&amp;rsquo;t necessarily need to be placed this way (in root path)&lt;/p>
&lt;h2 id="other-solutions">
&lt;a class="heading-anchor-link" href="#other-solutions">Other Solutions?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Use open source solutions - such as &lt;a href="https://github.com/overtrue/share.js" target="_blank" rel="noopener">https://github.com/overtrue/share.js&lt;/a>&lt;/li>
&lt;li>Write your own, because actual sharing media are few, commonly used are just WeChat, Weibo, and WeChat involves QR codes, most others are just links&lt;/li>
&lt;/ul></description></item><item><title>Git Team Development Process Specification</title><link>https://en.1991421.cn/2018/02/21/git-team-workflow/</link><pubDate>Wed, 21 Feb 2018 14:49:06 +0800</pubDate><guid>https://en.1991421.cn/2018/02/21/git-team-workflow/</guid><description>&lt;blockquote>
&lt;p>For multi-person collaborative programming in a team, Git repository management needs to have specifications. The following are the specifications used by my team.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>Continuously updated&lt;/code>&lt;/p>
&lt;h2 id="branch-naming-rules">
&lt;a class="heading-anchor-link" href="#branch-naming-rules">Branch Naming Rules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="branch-naming-rules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>Main branch: &lt;code>master&lt;/code> [Protected branch]&lt;/li>
&lt;li>Feature branch: &lt;code>feat/&amp;lt;name&amp;gt;&lt;/code>&lt;/li>
&lt;li>Bug fix branch: &lt;code>fix/&amp;lt;name&amp;gt;&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="operation-steps">
&lt;a class="heading-anchor-link" href="#operation-steps">Operation Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="operation-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>Create corresponding branch based on master&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Develop locally using &lt;code>git add&lt;/code>, &lt;code>git commit&lt;/code>, etc., push to remote repository, creating corresponding upstream branch.&lt;/p>
&lt;p>&lt;strong>&lt;em>Note:&lt;/em> Use syntax sugar like &lt;code>fixed #issueNum&lt;/code> in commit messages to associate issues. This way, when MR succeeds, the issue will automatically close, making it easier to track which code submission relates to which ticket&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CI service pulls target branch code for building, runs in internal environment, confirms OK&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Merge master code, if there are differences then handle with rebase merge, and re-validate functionality&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Initiate MR to &lt;code>master&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Administrator reviews, if there are modification points then develop corresponding modifications, and submit labeled as &lt;code>fixup:xxxx&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>After confirmation OK, merge into &lt;code>master&lt;/code>, delete original branch&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CI service pulls &lt;code>master&lt;/code> main branch code for building, deploys to production, and creates tag markers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If BUGs are encountered in production, immediately rollback during the release process, and repeat the above process in the repo to fix as soon as possible.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="explanation">
&lt;a class="heading-anchor-link" href="#explanation">Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Since branches are created locally by each developer and eventually pushed upstream, naming according to the above rules prevents branch naming conflicts&lt;/li>
&lt;li>Build releases automatically collect statistics on all submission modification points, so accurately filling out commit-message descriptions is particularly important&lt;/li>
&lt;/ul></description></item><item><title>Adding OpenSearch to a Website</title><link>https://en.1991421.cn/2018/02/19/7b570d4/</link><pubDate>Mon, 19 Feb 2018 20:33:27 +0800</pubDate><guid>https://en.1991421.cn/2018/02/19/7b570d4/</guid><description>&lt;blockquote>
&lt;p>If your site supports search, you can expose an OpenSearch descriptor so browsers detect it and prompt users to add your site as a search provider.&lt;/p>
&lt;/blockquote>
&lt;p>Here’s the process.&lt;/p>
&lt;h2 id="write-the-opensearch-description">
&lt;a class="heading-anchor-link" href="#write-the-opensearch-description">Write the OpenSearch description&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="write-the-opensearch-description"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>vi opensearch.xml&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;OpenSearchDescription&lt;/span> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://a9.com/-/spec/opensearch/1.1/&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;ShortName&amp;gt;&lt;/span>Forward Path&lt;span class="nt">&amp;lt;/ShortName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Description&amp;gt;&lt;/span>Article Search&lt;span class="nt">&amp;lt;/Description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Contact&amp;gt;&lt;/span>qianghe421@163.com&lt;span class="nt">&amp;lt;/Contact&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;InputEncoding&amp;gt;&lt;/span>UTF-8&lt;span class="nt">&amp;lt;/InputEncoding&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Url&lt;/span> &lt;span class="na">type=&lt;/span>&lt;span class="s">&amp;#34;text/html&amp;#34;&lt;/span> &lt;span class="na">template=&lt;/span>&lt;span class="s">&amp;#34;https://1991421.cn/tags/{searchTerms}&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Url&lt;/span> &lt;span class="na">type=&lt;/span>&lt;span class="s">&amp;#34;application/x-suggestions+json&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">template=&lt;/span>&lt;span class="s">&amp;#34;https://1991421.cn/tips?q={searchTerms}&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Image&lt;/span> &lt;span class="na">height=&lt;/span>&lt;span class="s">&amp;#34;32&amp;#34;&lt;/span> &lt;span class="na">width=&lt;/span>&lt;span class="s">&amp;#34;32&amp;#34;&lt;/span> &lt;span class="na">type=&lt;/span>&lt;span class="s">&amp;#34;image/x-icon&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>https://1991421.cn/favicon.ico&lt;span class="nt">&amp;lt;/Image&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/OpenSearchDescription&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="reference-it-in-html">
&lt;a class="heading-anchor-link" href="#reference-it-in-html">Reference it in HTML&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-it-in-html"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;application/opensearchdescription+xml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;opensearch.xml&amp;#34;&lt;/span> &lt;span class="na">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;Article Search&amp;#34;&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;search&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="suggestion-payload-format">
&lt;a class="heading-anchor-link" href="#suggestion-payload-format">Suggestion payload format&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="suggestion-payload-format"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;fir&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;firefox&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;first choice&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mozilla firefox&amp;#34;&lt;/span>&lt;span class="p">]]);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first item is the query string the user typed.&lt;/p>
&lt;p>This assumes your site already supports GET-based search. With that in place, continue.&lt;/p>
&lt;hr>
&lt;h2 id="browser-behavior">
&lt;a class="heading-anchor-link" href="#browser-behavior">Browser behavior&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-behavior"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="firefox">
&lt;a class="heading-anchor-link" href="#firefox">Firefox&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="firefox"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-19-131050.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="chrome">
&lt;a class="heading-anchor-link" href="#chrome">Chrome&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="chrome"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-19-134323.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="edge">
&lt;a class="heading-anchor-link" href="#edge">Edge&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="edge"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-24-050C8251FCECF01DFA151A8D0C235808.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Edge detects the provider, but it only lets you change the default search engine. The suggestions still come from Bing.&lt;/p>
&lt;h3 id="what-about-ie">
&lt;a class="heading-anchor-link" href="#what-about-ie">What about IE?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-about-ie"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Nope. IE8 supported custom providers, but IE9+ only lets you pick from the &lt;a href="https://www.microsoft.com/zh-cn/iegallery" target="_blank" rel="noopener">Microsoft gallery&lt;/a>. There’s no submission channel for custom providers anymore.&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This example uses just a subset of the spec. For more options, see the &lt;a href="http://www.opensearch.org/Specifications/OpenSearch/1.1" target="_blank" rel="noopener">OpenSearch documentation&lt;/a>.&lt;/p></description></item><item><title>Ionic: 2017-18 Roadmap</title><link>https://en.1991421.cn/2018/02/18/ionic-2017-18/</link><pubDate>Sun, 18 Feb 2018 21:46:26 +0800</pubDate><guid>https://en.1991421.cn/2018/02/18/ionic-2017-18/</guid><description>&lt;p>&lt;a href="https://blog.ionicframework.com/ionic-2017-18-roadmap/" target="_blank" rel="noopener">Original English Post&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-19-093916.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>The Ionic team has been extremely busy and highly productive this past fall. We&amp;rsquo;ve released several new projects and launched our Ionic Pro commercial service. We&amp;rsquo;ve been working hard to make our framework and services better and better.&lt;/p>
&lt;/blockquote>
&lt;p>With all these initiatives moving forward, I wanted to take a step back and talk about what Ionic will look like over the next 6-12 months.&lt;/p>
&lt;p>First, let&amp;rsquo;s go over some things you can expect in the near term.&lt;/p>
&lt;h2 id="next-30-60-days">
&lt;a class="heading-anchor-link" href="#next-30-60-days">Next 30-60 Days&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="next-30-60-days"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="ionic-11iphone-x">
&lt;a class="heading-anchor-link" href="#ionic-11iphone-x">Ionic 11/iPhone X&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ionic-11iphone-x"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>iPhone X launched in November, and iOS 11 has been out for over a month. We&amp;rsquo;ve released updates for Ionic 3.x for Angular and Ionic 1.x for AngularJS to ensure compatibility.&lt;/p>
&lt;p>For those still using the AngularJS version of Ionic, this is our first release in quite some time. Refer to our latest blog post and make sure to update correctly to the latest version.&lt;/p>
&lt;p>Ensure you have the latest versions of Ionic (AngularJS or Angular) for iPhone X and iOS 11:&lt;/p>
&lt;p>Ionic - Angular (3.x): 3.7.1
Ionic - AngularJS (1.x): 1.3.4 (&lt;a href="https://github.com/ionic-team/ionic-v1/issues/317#issuecomment-339357499" target="_blank" rel="noopener">Update Guide&lt;/a>)&lt;/p>
&lt;p>Read more about &lt;a href="http://blog.ionic.io/ios-11-checklist/" target="_blank" rel="noopener">preparing for iOS 11&lt;/a>&lt;/p>
&lt;h3 id="wkwebview-as-default-for-new-apps">
&lt;a class="heading-anchor-link" href="#wkwebview-as-default-for-new-apps">WKWebView as Default for New Apps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="wkwebview-as-default-for-new-apps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>We recently updated the default Web view on iOS to &lt;code>WKWebView&lt;/code>.&lt;/p>
&lt;p>WKWebView brings significant performance and feature improvements over the traditional UIWebView. Scrolling is better, infinite and virtual scrolling is faster and smoother, JavaScript execution is faster, and developers have access to more APIs.&lt;/p>
&lt;p>For existing apps, we recommend trying this out to see if your app benefits - we believe it will.&lt;/p>
&lt;p>However, there are some things to be aware of when using it.&lt;/p>
&lt;p>Read &lt;a href="http://blog.ionic.io/wkwebview-for-all-a-new-webview-for-ionic/" target="_blank" rel="noopener">the blog post about WKWebView&lt;/a> for more information.&lt;/p>
&lt;h3 id="ionic-pro-updates">
&lt;a class="heading-anchor-link" href="#ionic-pro-updates">Ionic Pro Updates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ionic-pro-updates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Ionic Pro is our new platform dedicated to making Ionic development faster and easier. Pro can automatically track runtime errors (even in TypeScript), enable app updates without going through the app store, and (soon) will offer PWA hosting, among other features.&lt;/p>
&lt;p>(&lt;a href="https://ionicframework.com/pro" target="_blank" rel="noopener">Learn more about Pro&lt;/a>)&lt;/p>
&lt;p>For users who encountered issues with Ionic Pro earlier, thank you for being among the first users and for your feedback. Expect rapid improvements to Pro as we&amp;rsquo;ve started hiring people onto the team.
Early reception has been positive, and the team has made many improvements to take your Ionic development to the next level.&lt;/p>
&lt;p>We&amp;rsquo;ve been actively working on major features recently, including PWA hosting, which will be rolled out to the public once it&amp;rsquo;s ready.&lt;/p>
&lt;h3 id="ionic-devapp-and-ionic-view">
&lt;a class="heading-anchor-link" href="#ionic-devapp-and-ionic-view">Ionic DevApp and Ionic View&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ionic-devapp-and-ionic-view"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>A few months ago, we discussed the Ionic View app, which is evolving into two apps: a new Ionic View focused on beta and external testing, and Ionic DevApp focused on local development.&lt;/p>
&lt;p>We saw that Ionic View was popular, with users either using it for local development or for external testing - this wasn&amp;rsquo;t surprising.&lt;/p>
&lt;p>What we&amp;rsquo;ve done is add more test cases to the new Ionic View. We&amp;rsquo;ve added extensive functionality to make it easy to share your app with external testers and devices, including A/B testing,
and sharing different versions with different people. We&amp;rsquo;ve also added many new native plugins to it. We&amp;rsquo;re confident this will be a major part of your beta testing, and you&amp;rsquo;ll find it especially simple compared to TestFlight and other similar tools.
Over the past week or so, we&amp;rsquo;ve also fixed some issues, so if you used it in October or earlier, please try it again.&lt;/p>
&lt;p>For the (soon-to-be-released) Ionic DevApp, we&amp;rsquo;ve taken the local development use case and improved it by 100x. Unlike server-side upload and in-app testing, DevApp discovers your ionic serve instances and seamlessly loads your app over the network.
It also supports hot reloading.&lt;/p>
&lt;p>I believe DevApp will be beneficial for the Ionic development workflow, as it removes the need to understand Cordova and the native SDK toolchain. Look forward to the official DevApp release in the coming weeks.&lt;/p>
&lt;h3 id="enterprise-ionic">
&lt;a class="heading-anchor-link" href="#enterprise-ionic">Enterprise Ionic&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enterprise-ionic"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Today, we&amp;rsquo;re expanding our work with enterprise companies using Ionic. Are you interested in getting expert help and scaling functionality in enterprise environments? Please &lt;a href="https://ionicframework.com/enterprise" target="_blank" rel="noopener">get in touch&lt;/a>&lt;/p>
&lt;h2 id="next-3-6-months">
&lt;a class="heading-anchor-link" href="#next-3-6-months">Next 3-6 Months&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="next-3-6-months"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="ionic-4">
&lt;a class="heading-anchor-link" href="#ionic-4">Ionic 4&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ionic-4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>We&amp;rsquo;re working hard on the next generation of Ionic, and you&amp;rsquo;ll see us porting most components to Web Components built using a new tool we created for building fast Web Components: &lt;a href="https://stenciljs.com/" target="_blank" rel="noopener">Stencil&lt;/a>.&lt;/p>
&lt;p>Ionic 4 is significant for several reasons, primarily because it will be the first &lt;code>framework-agnostic&lt;/code> version.&lt;/p>
&lt;p>For those who have been with us from the beginning, Ionic was the first Web standards component library. Unfortunately, before 2013, there was no &amp;ldquo;native&amp;rdquo; way to create custom HTML tags, so we chose AngularJS
because it was the best way to provide a custom tag library that didn&amp;rsquo;t require users to manually scaffold or initialize components. The Angular community ultimately became the ideal home for Ionic, and we&amp;rsquo;re very grateful for the support and help from the community that helped us achieve this goal.&lt;/p>
&lt;p>With Web Components now natively supported, we feel it&amp;rsquo;s time to return to our original dream and start making Ionic components available to all developers, regardless of their technology stack.&lt;/p>
&lt;p>&lt;code>Does this mean we're leaving Angular? No!&lt;/code> We think Angular is great, and our goal is to make &lt;code>ionic-angular&lt;/code> more like an API. This isn&amp;rsquo;t an either/or situation - instead, Ionic component tags will now run as Web Components rather than Angular code, which will make &lt;code>ionic-angular&lt;/code> smaller with faster load times.&lt;/p>
&lt;p>For those who like Ionic but want to use Vue, React, Ember, jQuery, or pure JavaScript, when Ionic 4 is released, you&amp;rsquo;ll be able to use Ionic. In fact, we&amp;rsquo;re certain you can use Ionic 4 with AngularJS (1.x), and we&amp;rsquo;ll provide a way for you to use it with Ionic/AngularJS.&lt;/p>
&lt;p>In addition to supporting these new frameworks, Ionic 4 will bring substantial improvements for PWAs, mainly due to &amp;ldquo;using the platform&amp;rdquo; (i.e., less code and using native browser components), and we&amp;rsquo;ve added new lazy loading strategies to Stencil.&lt;/p>
&lt;p>We hope that moving to Web Components will help &lt;a href="http://blog.ionic.io/the-end-of-framework-churn/" target="_blank" rel="noopener">end framework churn&lt;/a> once and for all.&lt;/p>
&lt;h3 id="cordova-plugin-initiative">
&lt;a class="heading-anchor-link" href="#cordova-plugin-initiative">Cordova Plugin Initiative&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cordova-plugin-initiative"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>We&amp;rsquo;re hiring people to proactively maintain Cordova plugins. We know the Cordova plugin ecosystem is facing challenges. Many plugins provide interfaces to native functionality, but many aren&amp;rsquo;t maintained or updated regularly. We want to invest more resources to help improve plugins and provide
some of the plugins we build to enterprises, along with supporting these plugins.&lt;/p>
&lt;h3 id="pwa">
&lt;a class="heading-anchor-link" href="#pwa">PWA&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pwa"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>It&amp;rsquo;s no secret that we&amp;rsquo;re fans of PWAs. Modern web apps use new APIs to provide app-like experiences for users.&lt;/p>
&lt;p>There&amp;rsquo;s a lot of confusion about PWAs. Does it mean Cordova is no longer necessary? Is Ionic losing app store support? Will PWAs replace native apps?&lt;/p>
&lt;p>In the web community, you&amp;rsquo;ll see people positioning PWAs as alternatives to native apps. At Ionic, you won&amp;rsquo;t see that. We believe the solution depends on what your users are looking for in your app. Do your users heavily rely on Google search and web links? A PWA can provide a high-quality experience for users without the hassle of pushing an app to the store. Do users find your app through the App Store or other apps?
A native app might make more sense. If your app requires more native functionality than web functionality, then a native app is also the best solution. If your app has the ability to provide a beautiful, simple web experience while also having extensive native APIs, you might understand that both PWAs and native apps are needed.&lt;/p>
&lt;h3 id="always-bet-on-the-web">
&lt;a class="heading-anchor-link" href="#always-bet-on-the-web">Always Bet on the Web&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="always-bet-on-the-web"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>With major updates to Web APIs and user devices becoming faster and better, it&amp;rsquo;s proven that betting on the web was right.&lt;/p>
&lt;p>We want Ionic to be the UI layer that runs on the web. We understand that native apps (App Store) are a very important platform for Ionic, but we also think it&amp;rsquo;s cool that the same code can work as a PWA. Additionally, Ionic apps can run in Electron as interactive web sites.&lt;/p>
&lt;p>We&amp;rsquo;ve always been 100% focused on open web standards and web technologies, even when it wasn&amp;rsquo;t cool, and we&amp;rsquo;re proud of that.&lt;/p>
&lt;p>Looking ahead, we&amp;rsquo;ll continue to push this pragmatic approach: focus on the web, run anywhere.&lt;/p>
&lt;p>Always bet on the web.&lt;/p>
&lt;p>Cheers,
Max&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This roadmap from Ionic&amp;rsquo;s leadership provides valuable insights into the strategic direction of the framework during a pivotal time in web and mobile development. The transition to Web Components with Ionic 4 represented a major shift towards framework-agnostic development, while maintaining strong support for Angular developers.&lt;/p>
&lt;p>The emphasis on both native app development (through Cordova plugins and app stores) and progressive web apps shows Ionic&amp;rsquo;s commitment to providing developers with flexible deployment options based on their specific use cases and target audiences.&lt;/p>
&lt;p>The investments in developer tooling like Ionic Pro, DevApp, and improved plugin maintenance demonstrate a mature approach to supporting the entire development lifecycle, not just the core framework itself.&lt;/p></description></item><item><title>GitHub Pages Custom Domain HTTPS Configuration</title><link>https://en.1991421.cn/2018/02/17/92147245/</link><pubDate>Sat, 17 Feb 2018 13:17:46 +0800</pubDate><guid>https://en.1991421.cn/2018/02/17/92147245/</guid><description>&lt;blockquote>
&lt;p>GitHub Pages itself supports HTTPS. For example, repository addresses like alanhg.github.io can be accessed normally via HTTPS, but after using a custom domain, HTTPS access is not available.
Here we use Cloudflare&amp;rsquo;s free CDN service to achieve this.&lt;/p>
&lt;/blockquote>
&lt;h2 id="operation-steps">
&lt;a class="heading-anchor-link" href="#operation-steps">Operation Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="operation-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>Register a Cloudflare account&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Click &amp;lsquo;Add Site&amp;rsquo;
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-052245.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Choose the free plan&lt;/p>
&lt;/li>
&lt;li>
&lt;p>After successful addition, it will prompt you to modify the domain&amp;rsquo;s DNS servers&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-052831.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Under Crypto, select &lt;code>SSL-Flexible&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-053052.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Check &lt;code>Always use HTTPS&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-053203.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Log in to your domain registrar&amp;rsquo;s control panel, modify DNS server addresses to those provided by Cloudflare above&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-053427.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After configuration is complete, accessing &lt;code>https://1991421.cn&lt;/code> works, but the browser will show a certificate error because it&amp;rsquo;s GitHub&amp;rsquo;s certificate, which doesn&amp;rsquo;t match our custom domain.
This issue requires some waiting time. It worked normally for me when I accessed it the next day.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-055144.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-17-055008.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>Angular 6 New Features and Improvements</title><link>https://en.1991421.cn/2018/02/14/angular6/</link><pubDate>Wed, 14 Feb 2018 00:08:28 +0800</pubDate><guid>https://en.1991421.cn/2018/02/14/angular6/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-02-13-160906.jpg"
alt="Angular"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>According to the plan, Angular 6 will be officially released at the end of March, with the current latest version being &lt;code>6.0.0-beta.3&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>Of course, Angular 6 compared to 5 will make development easier, with smaller bundle sizes and faster performance.&lt;/p>
&lt;h2 id="angular-6-improvements-and-features">
&lt;a class="heading-anchor-link" href="#angular-6-improvements-and-features">Angular 6 Improvements and Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="angular-6-improvements-and-features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>CLI Integration with Service Worker Support
&lt;ul>
&lt;li>&lt;code>ng generate universal &amp;lt;name&amp;gt;&lt;/code>&lt;/li>
&lt;li>&lt;code>ng build --app=&amp;lt;name&amp;gt;&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>CLI Improved Support for Universal and App Shell
&lt;ul>
&lt;li>&lt;code>ng generate app-shell [ --universal-app &amp;lt;universal-app-name&amp;gt;] [ --route &amp;lt;route&amp;gt;]&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Improved Decorator Error Messages&lt;/li>
&lt;li>TypeScript 2.5.x Support
&lt;ul>
&lt;li>&lt;code>npm install typescript@'~2.5.3'&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Many Valuable Features&lt;/li>
&lt;li>Added nativeElement Support&lt;/li>
&lt;li>Reintroduced Query Predicate&lt;/li>
&lt;li>Added Missing Lifecycle Tests for Projected Components&lt;/li>
&lt;li>Described Safety Worker&lt;/li>
&lt;li>Added afterContentInit and afterContentChecked&lt;/li>
&lt;li>Various Fixes for Language Service
&lt;ul>
&lt;li>TypeScript 2.6&amp;rsquo;s &lt;code>resolveModuleName&lt;/code> requires passed paths to be separated by &amp;lsquo;/&amp;rsquo;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Moved Init Hooks to TView&lt;/li>
&lt;li>Corrected onDestroy Order in Projected Components&lt;/li>
&lt;li>Added Types and Hooks for Directive Definitions&lt;/li>
&lt;li>Support for Bundle Size Tracking in CLI render3 Applications&lt;/li>
&lt;li>Fixed plat-detection Example Under Universal&lt;/li>
&lt;li>Added Canonical View Queries&lt;/li>
&lt;li>Compiler Improvements Regarding Reflect Changes&lt;/li>
&lt;li>Renamed &lt;strong>QueryPredicate&lt;/strong> to &lt;strong>LQuery&lt;/strong>&lt;/li>
&lt;li>Renamed &lt;strong>LQuery&lt;/strong> to &lt;strong>LQueries&lt;/strong> and Related&lt;/li>
&lt;li>&lt;strong>Allow HttpInterceptors to Inject HttpClient&lt;/strong>
&lt;ul>
&lt;li>Previously, injecting HttpClient in interceptors would cause circular dependency errors.&lt;/li>
&lt;li>Now you can directly declare HttpClient objects in the interceptor&amp;rsquo;s constructor&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Added navigationSource and restoredState to NavigationStart
&lt;ul>
&lt;li>Currently, NavigationStart cannot tell whether navigation was forcibly triggered or location changed&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Removed Comment Generation&lt;/li>
&lt;li>Fixed SideNav Height Issues on Narrow Screens&lt;/li>
&lt;/ul>
&lt;p>Original English article: &lt;a href="https://www.code-sample.com/2018/01/whats-new-in-angular-6.html" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;p>For more detailed information about features and bugs, please check the official repository: &lt;a href="https://github.com/angular/angular/blob/master/CHANGELOG.md" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Angular 6 brings significant improvements in developer experience, performance, and bundle size optimization. The enhanced CLI support for Service Workers and Universal rendering makes it easier to build modern web applications with better performance and SEO capabilities. These improvements continue Angular&amp;rsquo;s evolution toward a more efficient and developer-friendly framework.&lt;/p></description></item><item><title>Detecting Safari Private Browsing Mode</title><link>https://en.1991421.cn/2018/02/06/safari/</link><pubDate>Tue, 06 Feb 2018 21:59:47 +0800</pubDate><guid>https://en.1991421.cn/2018/02/06/safari/</guid><description>&lt;blockquote>
&lt;p>Cookies gave us personalization; HTML5 added &lt;code>localStorage&lt;/code> and &lt;code>sessionStorage&lt;/code>. Safari, however, blocks local storage APIs entirely when private browsing is enabled. Calls throw errors, so we may want to detect the mode and inform the user.&lt;/p>
&lt;/blockquote>
&lt;h2 id="code">
&lt;a class="heading-anchor-link" href="#code">Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">isPrivateMode&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">isPrivate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">openDatabase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isPrivate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">isPrivate&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">isPrivateMode&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Local storage is unavailable. Please disable private browsing.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://support.apple.com/HT203036&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="reference">
&lt;a class="heading-anchor-link" href="#reference">Reference&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Apple support article on private browsing: &lt;a href="https://support.apple.com/HT203036" target="_blank" rel="noopener">https://support.apple.com/HT203036&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I avoid touching &lt;code>localStorage&lt;/code> or &lt;code>sessionStorage&lt;/code> directly for detection because Safari 11 changed how those APIs behave. Probing &lt;code>openDatabase&lt;/code> still works reliably.&lt;/p></description></item><item><title>Upgrading Angular 4 to 5</title><link>https://en.1991421.cn/2018/01/27/angular45/</link><pubDate>Sat, 27 Jan 2018 14:47:23 +0800</pubDate><guid>https://en.1991421.cn/2018/01/27/angular45/</guid><description>&lt;blockquote>
&lt;p>Angular 5 was officially released on &lt;code>2017-11-01&lt;/code>, and Angular 4 was released on &lt;code>2017-03-23&lt;/code>, with a 7-month interval. Since the upgrade from Angular 4 to 5 is smooth with performance improvements and added features while maintaining browser compatibility, it&amp;rsquo;s worth upgrading.&lt;/p>
&lt;/blockquote>
&lt;p>Before upgrading, it&amp;rsquo;s necessary to understand what changes are involved.&lt;/p>
&lt;h2 id="what-are-the-main-changes-in-angular-5">
&lt;a class="heading-anchor-link" href="#what-are-the-main-changes-in-angular-5">What are the main changes in Angular 5?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-are-the-main-changes-in-angular-5"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Original source: &lt;a href="https://github.com/angular/angular/blob/master/CHANGELOG.md" target="_blank" rel="noopener">CHANGELOG&lt;/a>&lt;/p>
&lt;p>Here&amp;rsquo;s a brief overview:&lt;/p>
&lt;ol>
&lt;li>Features
&lt;ul>
&lt;li>HTTP requests can now directly set header information, form fields have new options like updateOn blur, routing adds ActivationStart/End events, etc.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Performance
&lt;ul>
&lt;li>Compiler improvements make incremental builds faster&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Breaking Changes
&lt;ul>
&lt;li>TypeScript requirement updated to 2.4.x&lt;/li>
&lt;li>Compiler has additional dependencies&lt;/li>
&lt;li>i18n uses new internationalization pipes, no longer needing separate intl imports&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="starting-the-upgrade">
&lt;a class="heading-anchor-link" href="#starting-the-upgrade">Starting the Upgrade&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="starting-the-upgrade"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Upgrade CLI to 1.5 or higher, latest version recommended
&lt;code>npm install -g @angular/cli&lt;/code>&lt;/li>
&lt;li>Upgrade project-related module packages&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/animations&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/common&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/compiler&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/core&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/forms&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/http&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/platform-browser&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/platform-browser-dynamic&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/router&amp;#34;: &amp;#34;^5.0.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;rxjs&amp;#34;: &amp;#34;^5.5.4&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/compiler-cli&amp;#34;: &amp;#34;^5.2.2&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;@angular/language-service&amp;#34;: &amp;#34;^4.4.6&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;codelyzer&amp;#34;: &amp;#34;~4.1.0&amp;#34;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;typescript&amp;#34;: &amp;#34;2.4.2&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="actual-aot-build-results">
&lt;a class="heading-anchor-link" href="#actual-aot-build-results">Actual AOT Build Results&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="actual-aot-build-results"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After upgrading, I tested AOT builds, and the bundle sizes are as follows:&lt;/p>
&lt;h3 id="angular-4">
&lt;a class="heading-anchor-link" href="#angular-4">Angular 4&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="angular-4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-27-075543.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="angular-5">
&lt;a class="heading-anchor-link" href="#angular-5">Angular 5&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="angular-5"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-27-075556.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Speaking purely of build bundle size, there&amp;rsquo;s almost no change, but the vendor file disappeared, with its content transferred to the main file.&lt;/strong>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Framework upgrades and version iterations are undoubtedly getting better, so we should approach framework upgrades with an open and progressive attitude, not being conservative or fearful. As long as it&amp;rsquo;s suitable and better, it&amp;rsquo;s worth upgrading.&lt;/p>
&lt;p>For example, after my quick upgrade, I immediately noticed several benefits, and there are certainly more:&lt;/p>
&lt;ol>
&lt;li>Regarding i18n changes, Angular removed intl and uses CLDR. The intl file (&lt;code>47KB&lt;/code>) that was previously needed for IE11 and below and Safari 10 and below is no longer required&lt;/li>
&lt;li>The vendor build file disappeared. Although the size is still counted in the main file, fewer files mean fewer browser requests, which is better to some extent&lt;/li>
&lt;li>Incremental build speed: Due to compiler improvements and TypeScript upgrades, compilation and building are inherently beneficial. Although I didn&amp;rsquo;t record detailed metrics due to time constraints, based on official announcements, it&amp;rsquo;s definitely faster.&lt;/li>
&lt;/ol></description></item><item><title>Jenkins Continuous Integration - Setup and Configuration</title><link>https://en.1991421.cn/2017/12/30/43304124/</link><pubDate>Sat, 30 Dec 2017 13:44:20 +0800</pubDate><guid>https://en.1991421.cn/2017/12/30/43304124/</guid><description>&lt;blockquote>
&lt;p>In actual project development and operations, I deeply realized how important it is to introduce continuous integration. After learning about continuous integration concepts and the Jenkins tool, I decided to set up the system to fill this gap and improve development efficiency.
No more talking, let&amp;rsquo;s get straight to the practical content.&lt;/p>
&lt;/blockquote>
&lt;h2 id="java-installation">
&lt;a class="heading-anchor-link" href="#java-installation">JAVA Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="java-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>Jenkins&lt;/code> is an automation server software written in JAVA, so we need to install &lt;code>JAVA&lt;/code> first.&lt;/p>
&lt;h3 id="download-jdk">
&lt;a class="heading-anchor-link" href="#download-jdk">Download JDK&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download-jdk"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" rel="noopener">http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&lt;/a>&lt;/p>
&lt;p>Download the &lt;code>jdk-8u152-linux-x64.tar.gz&lt;/code> package&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-30-063142.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Go to the server &lt;code>/usr/local&lt;/code>, create a java folder, put the package in, and extract it&lt;/p>
&lt;p>&lt;code>tar -zxvf jdk-8u152-linux-x64.tar.gz&lt;/code>&lt;/p>
&lt;h3 id="environment-variable-configuration">
&lt;a class="heading-anchor-link" href="#environment-variable-configuration">Environment Variable Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-variable-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>vi /etc/profile&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">JAVA_HOME&lt;/span>&lt;span class="o">=/&lt;/span>&lt;span class="n">usr&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">local&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">java&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">jdk1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">8.0&lt;/span>&lt;span class="n">_152&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CLASSPATH&lt;/span>&lt;span class="o">=.&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">JAVA_HOME&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">lib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">tools&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">jar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">PATH&lt;/span>&lt;span class="o">=$&lt;/span>&lt;span class="n">JAVA_HOME&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">export&lt;/span> &lt;span class="n">JAVA_HOME&lt;/span> &lt;span class="n">CLASSPATH&lt;/span> &lt;span class="n">PATH&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="tomcat-installation">
&lt;a class="heading-anchor-link" href="#tomcat-installation">Tomcat Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tomcat-installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Download the gz package, go to &lt;code>/usr/local&lt;/code>, create a Tomcat folder, put the package in, and extract it&lt;/p>
&lt;p>&lt;code>tar -xzvf apache-tomcat-9.0.2.tar.gz&lt;/code>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-30-063233.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="tomcat-service-configuration">
&lt;a class="heading-anchor-link" href="#tomcat-service-configuration">Tomcat Service Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tomcat-service-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since it&amp;rsquo;s not installed through the &lt;code>yum&lt;/code> package manager, it&amp;rsquo;s not yet service-ready. For actual production, it needs to be service-ready and auto-start, so we need to manually configure this.&lt;/p>
&lt;p>Modify the catalina.sh file in the /usr/local/tomcat/apache-tomcat-9.0.2/bin directory, add &lt;code>JAVAHOME&lt;/code> and &lt;code>CATALINA_HOME&lt;/code>&lt;/p>
&lt;p>Copy the file to the /etc/init.d directory and rename it to Tomcat&lt;/p>
&lt;p>&lt;code>sudo cp /usr/local/tomcat/apache-tomcat-9.0.2/bin/catalina.sh /etc/init.d/tomcat&lt;/code>&lt;/p>
&lt;h4 id="auto-start">Auto-start&lt;/h4>&lt;p>&lt;code>sudo chkconfig –add tomcat&lt;/code>&lt;/p>
&lt;h2 id="deploy-jenkins">
&lt;a class="heading-anchor-link" href="#deploy-jenkins">Deploy Jenkins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="deploy-jenkins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="download-war-package">
&lt;a class="heading-anchor-link" href="#download-war-package">Download WAR Package&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download-war-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Download address: &lt;a href="https://jenkins.io/download/" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;h3 id="extract">
&lt;a class="heading-anchor-link" href="#extract">Extract&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="extract"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Put the Jenkins.war package into &lt;code>/usr/local/tomcat/apache-tomcat-9.0.2/webapps&lt;/code>&lt;/p>
&lt;h3 id="restart-tomcat">
&lt;a class="heading-anchor-link" href="#restart-tomcat">Restart Tomcat&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="restart-tomcat"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>service tomcat restart&lt;/code>&lt;/p>
&lt;h3 id="web-access">
&lt;a class="heading-anchor-link" href="#web-access">WEB Access&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="web-access"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>http://192.168.1.81/jenkins/&lt;/p>
&lt;h3 id="install-essential-plugins">
&lt;a class="heading-anchor-link" href="#install-essential-plugins">Install Essential Plugins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-essential-plugins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since I want to integrate with GitLab, the plugins I installed are as follows:&lt;/p>
&lt;ol>
&lt;li>Email Extension Plugin &lt;code>Email functionality&lt;/code>&lt;/li>
&lt;li>GitLab Plugin &lt;code>GitLab related configuration&lt;/code>&lt;/li>
&lt;li>Publish Over SSH &lt;code>SSH connection to Linux servers&lt;/code>&lt;/li>
&lt;li>NodeJS Plugin &lt;code>NodeJs version management&lt;/code>&lt;/li>
&lt;/ol>
&lt;h2 id="create-jobs">
&lt;a class="heading-anchor-link" href="#create-jobs">Create Jobs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-jobs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Specific job configurations actually have many detailed points based on actual requirements. Here I&amp;rsquo;ll just briefly introduce my configuration.&lt;/p>
&lt;h3 id="source-code-management">
&lt;a class="heading-anchor-link" href="#source-code-management">Source Code Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="source-code-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Source code management selects Git. The authentication information below is a CI account I specifically created on GitLab. Since I&amp;rsquo;m doing internal network test deployment, the build branch is the dev branch.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-061636.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="build-triggers">
&lt;a class="heading-anchor-link" href="#build-triggers">Build Triggers&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-triggers"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Note here, I chose GitLab-push trigger build, which is GitLab&amp;rsquo;s Webhook. This address needs to be configured in the corresponding GitLab repository.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-061800.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-062038.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After configuring the hook, click test to confirm it&amp;rsquo;s OK&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-062114.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="build-environment">
&lt;a class="heading-anchor-link" href="#build-environment">Build Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>I&amp;rsquo;m doing frontend Node building here, so I select the required Node version&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-062158.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="build">
&lt;a class="heading-anchor-link" href="#build">Build&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Here I first execute package installation and build packaging, then transfer via SSH to the target path on the test server&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-062249.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="post-build">
&lt;a class="heading-anchor-link" href="#post-build">Post-build&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="post-build"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Implement email sending
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-062524.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="docker-installation-simplest-method">
&lt;a class="heading-anchor-link" href="#docker-installation-simplest-method">Docker Installation [&lt;code>Simplest Method&lt;/code>]&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="docker-installation-simplest-method"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above method is more complex but beneficial for fully understanding Jenkins deployment. A more efficient deployment method is using Docker.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>docker pull jenkinsci/blueocean&lt;/p>
&lt;/li>
&lt;li>
&lt;p>docker run&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">docker run &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --name jenkins-blueocean &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -p 8080:8080 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -p 50000:50000 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -v jenkins-data:/var/jenkins_home &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> jenkinsci/blueocean
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Access http://localhost:8080/login&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">docker&lt;/span> &lt;span class="n">exec&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">it&lt;/span> &lt;span class="n">jenkins&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">blueocean&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">bash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">cat&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">jenkins_home&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">secrets&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">initialAdminPassword&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Copy the password to the interface&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-20-064519.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The above only covers part of the story. Continuous integration is an ongoing process, a philosophy that requires continuous adjustment and configuration based on actual circumstances. I once read an article that explained this very well - standardized steps should be handed over to programs as much as possible, as this is more efficient and more secure. Humans inevitably make mistakes.&lt;/p>
&lt;p>Having attended DevOps conferences before, I&amp;rsquo;ve finally taken the first step.&lt;/p></description></item><item><title>How to Use jsPlumb (Step-by-Step Guide)</title><link>https://en.1991421.cn/2017/12/23/jsplumb/</link><pubDate>Sat, 23 Dec 2017 14:22:20 +0800</pubDate><guid>https://en.1991421.cn/2017/12/23/jsplumb/</guid><description>&lt;blockquote>
&lt;p>Recently, a project required implementing visual DAG (Directed Acyclic Graph) drawing. After researching several solutions, I ultimately chose jsPlumb.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>jsPlumb&lt;/code> itself is divided into the paid &lt;code>Toolkit&lt;/code> version and the &lt;code>Community&lt;/code> community edition. Since the core functionality is already available in the community edition, and the open-source community&amp;rsquo;s activity level makes future development and maintenance more convenient, I chose the community edition.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-23-064750.png"
alt="jsPlumb"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="learning-resources">
&lt;a class="heading-anchor-link" href="#learning-resources">Learning Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="learning-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When learning a technology, official documentation, official communities, and Google searches are what you should rely on most.&lt;/p>
&lt;p>Here are the addresses (repetitive, but important):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://jsplumbtoolkit.com/community/doc/home.html" target="_blank" rel="noopener">Official DOC&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://jsplumbtoolkit.com/demos.html" target="_blank" rel="noopener">Official DEMO&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jsplumb/jsplumb" target="_blank" rel="noopener">GitHub&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/jsplumb/jsplumb/blob/master/doc/wiki/changelog.md" target="_blank" rel="noopener">Changelog&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Besides these official resources, reading some blog posts can also be quite helpful. As I&amp;rsquo;m writing here, I hope it can help you somewhat.&lt;/p>
&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>My actual project uses Angular as the frontend framework, so the code here is written in that context. However, this shouldn&amp;rsquo;t significantly affect others from learning from it, as the thinking and concepts are similar - after all, it&amp;rsquo;s all JavaScript.&lt;/p>
&lt;/blockquote>
&lt;h2 id="first-understand-jsplumb-drawing-concepts">
&lt;a class="heading-anchor-link" href="#first-understand-jsplumb-drawing-concepts">First, Understand jsPlumb Drawing Concepts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="first-understand-jsplumb-drawing-concepts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>
&lt;p>&lt;strong>Endpoints&lt;/strong>
Endpoints refer to the visual points on our diagram that can have connections going out or coming in.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Anchors&lt;/strong>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Anchors refer to the positions where endpoints can ultimately land. This means an endpoint can specify multiple anchor positions, flexibly landing on a particular anchor based on the actual graphic position.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Connections&lt;/strong>
We establish relationships between multiple Windows (nodes) through connections. For example, whether we use Bezier curves or flowchart-style polyline connections requires setting up connectors.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Overlays&lt;/strong>
Solve UI issues above drawing and connections, such as labels or arrows.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Emphasis: &lt;code>Actually, it's easy to misunderstand the concepts of anchors and endpoints - I went down some wrong paths myself.&lt;/code>&lt;/p>
&lt;h2 id="examples">
&lt;a class="heading-anchor-link" href="#examples">Examples&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="examples"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="implemented-features">
&lt;a class="heading-anchor-link" href="#implemented-features">Implemented Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implemented-features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Dynamic node addition&lt;/li>
&lt;li>Dynamic node deletion&lt;/li>
&lt;li>Dynamic edge connection&lt;/li>
&lt;li>Dynamic edge deletion&lt;/li>
&lt;li>Node drag listening&lt;/li>
&lt;li>Node and edge right-click menus &lt;code>implemented with third-party component jquery.contextMenu&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="show-me-the-code">
&lt;a class="heading-anchor-link" href="#show-me-the-code">Show me the code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="show-me-the-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">Component&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ElementRef&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">OnInit&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Renderer2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ViewChild&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@angular/core&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">declare&lt;/span> &lt;span class="kd">let&lt;/span> &lt;span class="nx">jsPlumb&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">declare&lt;/span> &lt;span class="kd">let&lt;/span> &lt;span class="nx">$&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@Component&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selector&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;app-jsplumb&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">templateUrl&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;./jsplumb.component.html&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">styleUrls&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;./jsplumb.component.css&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">JsplumbComponent&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">OnInit&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">jsPlumbInstance&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">@ViewChild&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;canvas&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kr">public&lt;/span> &lt;span class="nx">panel&lt;/span>: &lt;span class="kt">ElementRef&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Canvas panel
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="nx">renderer&lt;/span>: &lt;span class="kt">Renderer2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ngOnInit() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">draw&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">draw() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">jsPlumb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getInstance&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// default drag options
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">DragOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">cursor&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;pointer&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">zIndex&lt;/span>: &lt;span class="kt">2000&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// the overlays to decorate each connection with. note that the label overlay uses a function to generate the label text; in this
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// case it returns the &amp;#39;labelText&amp;#39; member that we set on each connection in the &amp;#39;init&amp;#39; method below.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">ConnectionOverlays&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Arrow&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>: &lt;span class="kt">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">visible&lt;/span>: &lt;span class="kt">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">width&lt;/span>: &lt;span class="kt">11&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">length&lt;/span>: &lt;span class="kt">11&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;ARROW&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">events&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">click&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;you clicked on the arrow overlay&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Label&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>: &lt;span class="kt">0.1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;label&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cssClass&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;aLabel&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">events&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// connection.getOverlay(&amp;#34;label&amp;#34;)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">tap&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">prompt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Please enter label text:&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setLabel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Container&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;canvas&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ConnectionsDetachable&lt;/span>: &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">basicType&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">connector&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Bezier&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">curviness&lt;/span>: &lt;span class="kt">100&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">paintStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">stroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;red&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">strokeWidth&lt;/span>: &lt;span class="kt">4&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hoverPaintStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">stroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;blue&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">overlays&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Arrow&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">registerConnectionType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;basic&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">basicType&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Enable dragging
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">draggable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">draggable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">draggable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow3&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">draggable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow4&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Add endpoints
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEndpoint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sourceEndpoint&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEndpoint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow2&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">targetEndpoint&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// listen for clicks on connections, and offer to delete connections on click.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">//
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;click&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">conn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">originalEvent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// if (confirm(&amp;#34;Delete connection from &amp;#34; + conn.sourceId + &amp;#34; to &amp;#34; + conn.targetId + &amp;#34;?&amp;#34;))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// instance.detach(conn);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// conn.toggleType(&amp;#39;basic&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">conn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">originalEvent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">//
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connection&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addMenu4Edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connectionDetached&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connectionDrag&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connection &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39; is being dragged. suspendedElement is &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">suspendedElement&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39; of type &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">suspendedElementType&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connectionDragStop&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connection &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39; was dragged&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connectionMoved&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connection &amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39; was moved&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;click&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">detach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Add right-click menu for edges
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">addMenu4Edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">connInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addClass&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">removeEdge&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">connInfo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">cons&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getConnections&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;*&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">source&lt;/span>: &lt;span class="kt">connInfo&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;sourceId&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">target&lt;/span>: &lt;span class="kt">connInfo&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;targetId&amp;#39;&lt;/span>&lt;span class="p">]});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">deleteConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">cons&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">$&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">contextMenu&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selector&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;.&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">connInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">connection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">callback&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`event`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">items&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;cut&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Delete&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;cut&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">callback&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeEdge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Add right-click menu for nodes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * id=nodeProgram-5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">addMenu4Node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodeId&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">removeNode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">remove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodeId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">$&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">contextMenu&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selector&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">nodeId&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">callback&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`event`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">items&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;cut&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Delete&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">icon&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;cut&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">callback&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">opt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">addNode() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Add node information to chart
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">div&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">renderer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;div&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">div&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;flowchartWindow5&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">div&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">innerHTML&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`&amp;lt;strong&amp;gt;End 5&amp;lt;/strong&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">div&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;class&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;window jtk-node&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">renderer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">appendChild&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">panel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nativeElement&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">div&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEndpoint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;flowchartWindow5&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">sourceEndpoint&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Enable dragging
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">draggable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">$&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">div&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">drag&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">start&lt;/span>: &lt;span class="kt">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Right-click menu
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addMenu4Node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">div&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Delete edge
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">removeEdge() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">cons&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getConnections&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;*&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">source&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;flowchartWindow1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;flowchartWindow2&amp;#39;&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">jsPlumbInstance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">deleteConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">cons&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">anchors&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">0.2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">connectorPaintStyle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">strokeWidth&lt;/span>: &lt;span class="kt">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">stroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#61B7CF&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">joinstyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;round&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">outlineStroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;white&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">outlineWidth&lt;/span>: &lt;span class="kt">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// .. and this is the hover style.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">connectorHoverStyle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">strokeWidth&lt;/span>: &lt;span class="kt">3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">stroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#216477&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">outlineWidth&lt;/span>: &lt;span class="kt">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">outlineStroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;white&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">endpointHoverStyle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fill&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#216477&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">stroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#216477&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// the definition of source endpoints (the small blue ones)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">sourceEndpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">endpoint&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Dot&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">paintStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">stroke&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#7AB02C&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fill&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;transparent&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">radius&lt;/span>: &lt;span class="kt">7&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">strokeWidth&lt;/span>: &lt;span class="kt">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isSource&lt;/span>: &lt;span class="kt">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">connector&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Flowchart&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">stub&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">40&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">60&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">gap&lt;/span>: &lt;span class="kt">10&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cornerRadius&lt;/span>: &lt;span class="kt">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">alwaysRespectStubs&lt;/span>: &lt;span class="kt">true&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">connectorStyle&lt;/span>: &lt;span class="kt">connectorPaintStyle&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hoverPaintStyle&lt;/span>: &lt;span class="kt">endpointHoverStyle&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">connectorHoverStyle&lt;/span>: &lt;span class="kt">connectorHoverStyle&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dragOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">overlays&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Label&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">0.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Drag&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cssClass&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;endpointSourceLabel&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">visible&lt;/span>: &lt;span class="kt">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// the definition of target endpoints (will appear when the user drags a connection)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">targetEndpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">endpoint&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Dot&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">paintStyle&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">fill&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;#7AB02C&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">radius&lt;/span>: &lt;span class="kt">7&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">hoverPaintStyle&lt;/span>: &lt;span class="kt">endpointHoverStyle&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">maxConnections&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dropOptions&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">hoverClass&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;hover&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">activeClass&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;active&amp;#39;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isTarget&lt;/span>: &lt;span class="kt">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">overlays&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;Label&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mf">0.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mf">0.5&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Drop&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">cssClass&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;endpointTargetLabel&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">visible&lt;/span>: &lt;span class="kt">false&lt;/span>&lt;span class="p">}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>jsPlumb&lt;/code> is relatively mature compared to other drawing solutions. If you don&amp;rsquo;t want to reinvent the wheel, using this can meet your needs. However, in practical use, I personally think the official documentation is still not perfect - sometimes it&amp;rsquo;s confusing, and sometimes it contains errors.&lt;/p>
&lt;p>For example, the function for deleting edges is actually called &lt;code>deleteConnection&lt;/code>, as shown below:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-23-065854.png"
alt="deleteConnection"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p></description></item><item><title>Multi-platform Blog Hosting with GitHub and Coding</title><link>https://en.1991421.cn/2017/12/14/github-codingblog/</link><pubDate>Thu, 14 Dec 2017 12:45:16 +0800</pubDate><guid>https://en.1991421.cn/2017/12/14/github-codingblog/</guid><description>&lt;blockquote>
&lt;p>GitHub provides static page hosting services, so I&amp;rsquo;ve always used GitHub to host my personal static blog.&lt;/p>
&lt;/blockquote>
&lt;p>On GitHub, I created a user repository with the master branch for static pages and the source branch for blog source code.&lt;/p>
&lt;p>The blogging workflow is:&lt;/p>
&lt;ol>
&lt;li>Write locally, push to source branch&lt;/li>
&lt;li>Travis-CI detects source branch changes, builds and updates the master branch&lt;/li>
&lt;/ol>
&lt;p>Overall, this is very convenient, but GitHub blocks Baidu crawlers. Since Baidu is still widely used in China, not being indexed by Baidu is quite frustrating. So I had to consider mirroring a static site to a domestic platform or rent my own server for hosting.&lt;/p>
&lt;p>I chose to use &lt;code>Coding&lt;/code> for two main reasons:&lt;/p>
&lt;ol>
&lt;li>Fast speed&lt;/li>
&lt;li>Supports custom domains&lt;/li>
&lt;/ol>
&lt;h2 id="implementation-strategy">
&lt;a class="heading-anchor-link" href="#implementation-strategy">Implementation Strategy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation-strategy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="creating-coding-account">
&lt;a class="heading-anchor-link" href="#creating-coding-account">Creating Coding Account&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="creating-coding-account"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Recommend using the same username as GitHub, though different names work too. Just note that the repository name must match the account name to support &lt;code>{user_name}.coding.me&lt;/code> access.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-14-054602.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="add-multiple-upstream-repository-addresses-locally">
&lt;a class="heading-anchor-link" href="#add-multiple-upstream-repository-addresses-locally">Add Multiple Upstream Repository Addresses Locally&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-multiple-upstream-repository-addresses-locally"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-14-054315.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="push-static-pages-to-coding-main-branch">
&lt;a class="heading-anchor-link" href="#push-static-pages-to-coding-main-branch">Push Static Pages to Coding Main Branch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="push-static-pages-to-coding-main-branch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ git push coding master
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="enable-pages-service-and-configure-custom-domain">
&lt;a class="heading-anchor-link" href="#enable-pages-service-and-configure-custom-domain">Enable Pages Service and Configure Custom Domain&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enable-pages-service-and-configure-custom-domain"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-14-054144.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="add-baidu-route-resolution-to-alibaba-dns-domain-resolution">
&lt;a class="heading-anchor-link" href="#add-baidu-route-resolution-to-alibaba-dns-domain-resolution">Add Baidu Route Resolution to Alibaba DNS Domain Resolution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-baidu-route-resolution-to-alibaba-dns-domain-resolution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-14-054107.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;strong>Note: I only want to solve the issue of Baidu crawlers being unable to index, so I only set the Baidu search route to go through the Coding site.&lt;/strong>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After the above configuration, Baidu crawlers can normally access our website, and Baidu indexing will be OK.&lt;/p></description></item><item><title>Full-Stack Deployment: Angular + Express on CentOS</title><link>https://en.1991421.cn/2017/12/13/angular-express/</link><pubDate>Wed, 13 Dec 2017 17:53:05 +0800</pubDate><guid>https://en.1991421.cn/2017/12/13/angular-express/</guid><description>&lt;blockquote>
&lt;p>I recently needed to deploy a web application on a cloud server. Here are my documented notes for anyone with similar requirements.&lt;/p>
&lt;/blockquote>
&lt;h2 id="deployment-environment">
&lt;a class="heading-anchor-link" href="#deployment-environment">Deployment Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="deployment-environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Server OS&lt;/strong>: &lt;code>CentOS release 6.8 (Final)&lt;/code>&lt;/li>
&lt;li>&lt;strong>Network&lt;/strong>: &lt;code>Connected to Internet&lt;/code>&lt;/li>
&lt;li>&lt;strong>Development Machine&lt;/strong>: &lt;code>Windows&lt;/code> (or macOS)&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="1-terminal-connection">
&lt;a class="heading-anchor-link" href="#1-terminal-connection">1. Terminal Connection&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-terminal-connection"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use a terminal emulator like &lt;strong>PuTTY&lt;/strong> (Windows) or &lt;strong>iTerm2&lt;/strong> (macOS) to establish an SSH connection to your target server.&lt;/p>
&lt;h2 id="2-setting-up-nodejs-with-nvm">
&lt;a class="heading-anchor-link" href="#2-setting-up-nodejs-with-nvm">2. Setting Up Node.js with NVM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-setting-up-nodejs-with-nvm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>While you can install Node.js directly, using &lt;strong>NVM&lt;/strong> (Node Version Manager) is highly recommended for easy version management.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh &lt;span class="p">|&lt;/span> bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note: After installation, you may need to restart your terminal session for the &lt;code>nvm&lt;/code> command to be recognized.&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install the Node.js LTS version (e.g., version 6 in this example)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nvm install &lt;span class="m">6&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="optimizing-npm-with-nrm">
&lt;a class="heading-anchor-link" href="#optimizing-npm-with-nrm">Optimizing NPM with NRM&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="optimizing-npm-with-nrm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;strong>NRM&lt;/strong> helps manage npm registries. Switching to a local mirror (like Taobao for developers in China) can significantly speed up package downloads.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm i nrm -g
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nrm use taobao
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="3-preparing-the-project-directory">
&lt;a class="heading-anchor-link" href="#3-preparing-the-project-directory">3. Preparing the Project Directory&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-preparing-the-project-directory"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>According to the Linux Filesystem Hierarchy Standard, &lt;code>/var&lt;/code> is a good place for variable data like web applications.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir /var/www/my-project
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="uploading-files">
&lt;a class="heading-anchor-link" href="#uploading-files">Uploading Files&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="uploading-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Upload your &lt;strong>Frontend &lt;code>dist&lt;/code> folder&lt;/strong>, &lt;strong>Backend source code&lt;/strong>, and optionally &lt;code>node_modules&lt;/code> (though running &lt;code>npm install&lt;/code> on the server is usually cleaner) using tools like &lt;strong>WinSCP&lt;/strong> (Windows) or &lt;strong>Cyberduck/SCP&lt;/strong> (macOS).&lt;/p>
&lt;h2 id="4-process-management-with-pm2">
&lt;a class="heading-anchor-link" href="#4-process-management-with-pm2">4. Process Management with PM2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="4-process-management-with-pm2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;strong>PM2&lt;/strong> is a production-grade process manager for Node.js applications. It ensures your app stays alive and restarts if it crashes.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm i pm2 -g
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="configuring-environment-variables">
&lt;a class="heading-anchor-link" href="#configuring-environment-variables">Configuring Environment Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuring-environment-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>To distinguish between development and production configurations in your Express app:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">vi /etc/profile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add the following line at the end:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Apply the changes immediately:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> /etc/profile
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="starting-the-application">
&lt;a class="heading-anchor-link" href="#starting-the-application">Starting the Application&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="starting-the-application"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> /var/www/my-project
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pm2 start app.js --name&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;my-app&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ensure the app restarts if the server reboots:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pm2 save
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pm2 startup
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="5-web-server-setup-nginx">
&lt;a class="heading-anchor-link" href="#5-web-server-setup-nginx">5. Web Server Setup: Nginx&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="5-web-server-setup-nginx"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Nginx will act as a reverse proxy, sitting in front of our Node.js application.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">yum -y install nginx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="reverse-proxy-configuration">
&lt;a class="heading-anchor-link" href="#reverse-proxy-configuration">Reverse Proxy Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reverse-proxy-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Edit the configuration file at &lt;code>/etc/nginx/conf.d/default.conf&lt;/code>. We want Nginx to listen on port 80 and forward traffic to our app (running on port 3001 in this example).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">server&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">listen&lt;/span> &lt;span class="mi">80&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">server_name&lt;/span> &lt;span class="s">example.com&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1"># Replace with your domain
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">location&lt;/span> &lt;span class="s">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_pass&lt;/span> &lt;span class="s">http://localhost:3001&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_http_version&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="s">.1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_set_header&lt;/span> &lt;span class="s">Upgrade&lt;/span> &lt;span class="nv">$http_upgrade&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_set_header&lt;/span> &lt;span class="s">Connection&lt;/span> &lt;span class="s">&amp;#39;upgrade&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_set_header&lt;/span> &lt;span class="s">Host&lt;/span> &lt;span class="nv">$host&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">proxy_cache_bypass&lt;/span> &lt;span class="nv">$http_upgrade&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="managing-nginx">
&lt;a class="heading-anchor-link" href="#managing-nginx">Managing Nginx&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="managing-nginx"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">service nginx start &lt;span class="c1"># Start service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chkconfig nginx on &lt;span class="c1"># Enable auto-start on boot&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">nginx -s reload &lt;span class="c1"># Reload config after changes&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="6-firewall-configuration">
&lt;a class="heading-anchor-link" href="#6-firewall-configuration">6. Firewall Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="6-firewall-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Ensure port 80 is open to external traffic:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">/sbin/iptables -I INPUT -p tcp --dport &lt;span class="m">80&lt;/span> -j ACCEPT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/rc.d/init.d/iptables save
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-13-104640.png"
alt="Deployment Success"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Deployment complete! You can now access your application via the server&amp;rsquo;s IP address or domain name. While this guide was written based on a Windows environment, the server-side steps are identical for macOS users.&lt;/p></description></item><item><title>ES6 Common Issues</title><link>https://en.1991421.cn/2017/12/12/es6-common-issues/</link><pubDate>Tue, 12 Dec 2017 19:06:23 +0800</pubDate><guid>https://en.1991421.cn/2017/12/12/es6-common-issues/</guid><description>&lt;blockquote>
&lt;p>The emergence of ES6 (ECMAScript2015) has brought new surprises to front-end developers. It contains some great new features that make it easier to implement many complex operations and improve developer efficiency.
Since I use Node and Angular, I can directly use ES6. I encountered some issues during actual use, so I&amp;rsquo;m noting them here.&lt;/p>
&lt;/blockquote>
&lt;h2 id="adding-dynamic-keys-to-objects">
&lt;a class="heading-anchor-link" href="#adding-dynamic-keys-to-objects">Adding Dynamic Keys to Objects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-dynamic-keys-to-objects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names" target="_blank" rel="noopener">Computed Property Names&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;DYNAMIC_KEY&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">obj&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;ES6!&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;gt; { &amp;#39;DYNAMIC_KEY&amp;#39;: &amp;#39;ES6!&amp;#39; }
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Uninstall Node.js on macOS</title><link>https://en.1991421.cn/2017/11/29/macnode/</link><pubDate>Wed, 29 Nov 2017 23:17:53 +0800</pubDate><guid>https://en.1991421.cn/2017/11/29/macnode/</guid><description>&lt;blockquote>
&lt;p>I initially installed Node on macOS, and later discovered nvm. The Node versions managed by nvm coexist with the system-installed version, which can be messy.
For example, I installed many global CLIs under a specific nvm version and set it as default, but sometimes a new terminal session still defaults to the system Node. Switching every time is a waste. The best approach is to remove the system Node and manage everything with nvm.&lt;/p>
&lt;/blockquote>
&lt;h2 id="how-to-uninstall-node">
&lt;a class="heading-anchor-link" href="#how-to-uninstall-node">How to uninstall Node?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-uninstall-node"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If your Node version is managed by nvm, uninstalling is easy: run &lt;code>nvm uninstall version&lt;/code>. If you installed via Homebrew, run &lt;code>brew uninstall node&lt;/code>.
If you installed the pkg from the official website, removal is more tedious. See below.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-11-29-152926.jpg"
alt="nvm ls"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Note that in &lt;code>nvm ls&lt;/code>, &lt;code>system&lt;/code> is the Node installed on the system.&lt;/p>
&lt;h2 id="manually-uninstall-node-installed-via-pkg">
&lt;a class="heading-anchor-link" href="#manually-uninstall-node-installed-via-pkg">Manually uninstall Node installed via pkg&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="manually-uninstall-node-installed-via-pkg"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="remove-node-files-under-usrlocallib">
&lt;a class="heading-anchor-link" href="#remove-node-files-under-usrlocallib">Remove Node files under &lt;code>/usr/local/lib&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="remove-node-files-under-usrlocallib"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo rm -rf /usr/local/lib/node /usr/local/lib/node_modules /var/db/receipts/org.nodejs.*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ &lt;span class="nb">cd&lt;/span> /usr/local/lib
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo rm -rf node*
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="enter-usrlocalinclude-and-delete-directories-containing-node-and-node_modules">
&lt;a class="heading-anchor-link" href="#enter-usrlocalinclude-and-delete-directories-containing-node-and-node_modules">Enter &lt;code>/usr/local/include&lt;/code> and delete directories containing &lt;code>node&lt;/code> and &lt;code>node_modules&lt;/code>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enter-usrlocalinclude-and-delete-directories-containing-node-and-node_modules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">cd /usr/local/include
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo rm -rf node*
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="go-to-your-home-folder-check-locallibinclude-folders-and-delete-items-that-include-node-or-node_modules">
&lt;a class="heading-anchor-link" href="#go-to-your-home-folder-check-locallibinclude-folders-and-delete-items-that-include-node-or-node_modules">Go to your home folder, check local/lib/include folders, and delete items that include node or node_modules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="go-to-your-home-folder-check-locallibinclude-folders-and-delete-items-that-include-node-or-node_modules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h3 id="enter-usrlocalbin-and-delete-the-node-executables">
&lt;a class="heading-anchor-link" href="#enter-usrlocalbin-and-delete-the-node-executables">Enter &lt;code>/usr/local/bin&lt;/code> and delete the Node executables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="enter-usrlocalbin-and-delete-the-node-executables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">cd /usr/local/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo rm -rf /usr/local/bin/npm
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo rm -rf /usr/local/bin/node
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ls -las # Carefully check, globally installed npm packages usually create symlinks in this directory; delete them if found
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="other-cleanup">
&lt;a class="heading-anchor-link" href="#other-cleanup">Other cleanup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="other-cleanup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo rm -rf /usr/local/share/man/man1/node.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo rm -rf /usr/local/lib/dtrace/node.d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo rm -rf ~/.npm
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note&lt;/em>: &lt;code>/usr/local/lib&lt;/code> and &lt;code>/usr/local/bin&lt;/code> contain many symlinks created by globally installed npm packages. Remove them carefully.&lt;/p>
&lt;p>Once everything is done, check &lt;code>nvm ls&lt;/code> and you should see that the system version is gone.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-11-29-153057.png"
alt="nvm ls"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="nvm-command-not-found">
&lt;a class="heading-anchor-link" href="#nvm-command-not-found">nvm command not found&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="nvm-command-not-found"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After uninstalling as above, you might see this error.&lt;/p>
&lt;h3 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Apply immediately: run &lt;code>source ~/.nvm/nvm.sh&lt;/code>. This will not persist across new sessions or reboots, so it is recommended to add the line to &lt;code>~/.bashrc&lt;/code> or &lt;code>~/.profile&lt;/code> so it loads at startup.&lt;/p></description></item><item><title>Git Multiple Remote Repository Configuration</title><link>https://en.1991421.cn/2017/11/27/git-multiple-remote-repos/</link><pubDate>Mon, 27 Nov 2017 22:31:14 +0800</pubDate><guid>https://en.1991421.cn/2017/11/27/git-multiple-remote-repos/</guid><description>&lt;blockquote>
&lt;p>In actual development, sometimes you may encounter situations where Git projects need to be hosted on multiple remote addresses. Fortunately, Git supports multiple upstreams, so the operation is quite simple.&lt;/p>
&lt;/blockquote>
&lt;h2 id="example">
&lt;a class="heading-anchor-link" href="#example">Example&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="example"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For example, we have GitHub and Gitee (formerly known as OSChina) repository addresses&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add GitHub&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git remote add github https://github.com/alanhe421/NiceFish.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add Gitee&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git remote add oschina https://gitee.com/mumu-osc/NiceFish.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Push to GitHub&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push github master
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Push to multiple repositories&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push --all
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="syntax-explanation">
&lt;a class="heading-anchor-link" href="#syntax-explanation">Syntax Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="syntax-explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For detailed information, please refer to the official manual: &lt;a href="https://git-scm.com/docs/git-push" target="_blank" rel="noopener">Link&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># name is the remote name, url is the address
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git remote add &amp;lt;name&amp;gt; &amp;lt;url&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># --all, push all branches, repository is the repository address or remote name, refspec is the branch name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push [--all] [&amp;lt;repository&amp;gt; [&amp;lt;refspec&amp;gt;…​]]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>oh-my-zsh Chinese Character Encoding Issues</title><link>https://en.1991421.cn/2017/11/26/oh-my-zsh/</link><pubDate>Sun, 26 Nov 2017 20:54:50 +0800</pubDate><guid>https://en.1991421.cn/2017/11/26/oh-my-zsh/</guid><description>&lt;blockquote>
&lt;p>zsh is a powerful shell, but its configuration is too complex for many people, causing them to hesitate. Until one day, a master developer created a project that makes it easy to get started - that&amp;rsquo;s &lt;code>oh my zsh&lt;/code>. To learn more about it, visit the &lt;a href="https://github.com/robbyrussell/oh-my-zsh" target="_blank" rel="noopener">official repository&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-11-26-132719.jpg"
alt="oh-my-zsh"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After installing oh-my-zsh, I found that Chinese filenames would display as garbled characters. This is definitely a character encoding issue. After some research, here&amp;rsquo;s the solution:&lt;/p>
&lt;ol>
&lt;li>&lt;code>vi ~/.zshrc&lt;/code>&lt;/li>
&lt;li>Add the following two lines at the end:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LC_ALL&lt;/span>&lt;span class="o">=&lt;/span>en_US.UTF-8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">LANG&lt;/span>&lt;span class="o">=&lt;/span>en_US.UTF-8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>&lt;code>source ~/.zshrc&lt;/code> to make it take effect immediately. Enter Chinese characters again and you&amp;rsquo;ll find it&amp;rsquo;s fixed.&lt;/li>
&lt;/ol></description></item><item><title>How to Use dagre-d3 (Step-by-Step Guide)</title><link>https://en.1991421.cn/2017/11/04/d3-dagre/</link><pubDate>Sat, 04 Nov 2017 19:54:27 +0800</pubDate><guid>https://en.1991421.cn/2017/11/04/d3-dagre/</guid><description>&lt;blockquote>
&lt;p>Recently, a project required implementing frontend flowchart drawing, so I conducted a systematic technical research including echarts, highcharts, jsPlumbs, jointJS, RaphaelJs, d3, etc. echarts and highcharts don&amp;rsquo;t have good flowchart models themselves and are more suitable for bar charts, pie charts, etc., so they were directly passed. After comparison, D3 was finally chosen.&lt;/p>
&lt;/blockquote>
&lt;p>Excerpt from a blog post ranking, the author summarized these flowchart libraries, showing that D3 is still very powerful.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-11-04-121118.png"
alt="Score board"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Blog post link, &lt;a href="https://www.erp5.com/javascript-10.Flow.Chart" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;p>After deciding to use D3, writing directly with D3 would be too laborious. After searching, I found a D3-based library - dagre-d3. During use, as I delved deeper, I gained a thorough understanding of this library. Whether searching on Baidu or Google, I found that there&amp;rsquo;s too little material, especially in Chinese.
Here, I&amp;rsquo;ll summarize it.&lt;/p>
&lt;h2 id="dagre-d3---what-you-need-to-know">
&lt;a class="heading-anchor-link" href="#dagre-d3---what-you-need-to-know">dagre-d3 - What You Need to Know&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="dagre-d3---what-you-need-to-know"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="what-is-it">
&lt;a class="heading-anchor-link" href="#what-is-it">What is it?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-it"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>Dagre&lt;/code> is a &lt;code>JavaScript&lt;/code> library that can easily create flowcharts on the client side, and dagre-d3 can be understood as the frontend of Dagre, using D3 for rendering.&lt;/p>
&lt;h3 id="project-activity">
&lt;a class="heading-anchor-link" href="#project-activity">Project Activity&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="project-activity"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>dagre-d3&lt;/code> has 1K+ stars, indicating this library is still quite popular. However, both dagre and d3-dagre are in an inactive state, and the author himself is no longer maintaining it.&lt;/p>
&lt;h2 id="demo">
&lt;a class="heading-anchor-link" href="#demo">Demo&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="demo"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Here&amp;rsquo;s a simple demo to illustrate:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-11-04-142637.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;a href="https://github.com/alanhe421/angular-demo" target="_blank" rel="noopener">View complete source code here&lt;/a>&lt;/p>
&lt;h3 id="show-me-the-code">
&lt;a class="heading-anchor-link" href="#show-me-the-code">Show me the code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="show-me-the-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;container&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;col-sm-6&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span> &lt;span class="na">width&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">960&lt;/span> &lt;span class="na">height&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">400&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">g&lt;/span>&lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">svg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;svg&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Create the input graph
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">dagreD3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">graphlib&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Graph&lt;/span>&lt;span class="p">({});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Set an object for the graph label
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setGraph&lt;/span>&lt;span class="p">({});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Default to assigning a new object as a label for each new edge.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setDefaultEdgeLabel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">graph&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">transition&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">selection&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">selection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">transition&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">duration&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Zoom functionality implementation
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">zoom&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">behavior&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">zoom&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;zoom&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">inner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;transform&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;translate(&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">translate&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;)&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;scale(&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scale&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;)&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">zoom&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;VVV&amp;#39;&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;A&amp;#34;&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;B&amp;#34;&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setNode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">labelType&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="s2">&amp;#34;html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;i class=\&amp;#34;fa fa-database\&amp;#34;&amp;gt;&amp;lt;/i&amp;gt;B&amp;#34;&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setEdge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setEdge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setEdge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Run the renderer. This is what draws the final graph.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">inner&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nodes&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">node&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Node &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">: Label:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">,X:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">,Y:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">//give IDs to each of the nodes so that they can be accessed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g.node rect&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;node&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g.edgePath path&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">v&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;-&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g.edgeLabel g&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;label_&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">v&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;-&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nodes&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">node&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">customId&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;node&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">edges&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">edge&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">w&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">edge&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">customId&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">v&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;-&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">w&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// code for drag
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">dragstart&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sourceEvent&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stopPropagation&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">dragmover&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">currentThis&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dragmove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">currentThis&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">dragmove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dragmover&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">nodeDrag&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">behavior&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">drag&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;dragstart&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dragstart&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;drag&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dragmove&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">edgeDrag&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">behavior&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">drag&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;dragstart&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dragstart&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;drag&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">translateEdge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">w&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dy&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">$&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;#&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">g&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">edge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">w&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">customId&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;d&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">calcPoints&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">nodeDrag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g.node&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">edgeDrag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g.edgePath&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="main-functions">
&lt;a class="heading-anchor-link" href="#main-functions">Main Functions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="main-functions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For usage, I recommend directly looking at the d3-dagre source code to avoid missing anything. Here are the main functions:&lt;/p>
&lt;ul>
&lt;li>Add node &lt;code>setNode(v, {label: 'VVV'})&lt;/code>&lt;/li>
&lt;li>Add edge &lt;code>setEdge(v, s)&lt;/code>&lt;/li>
&lt;li>Delete node &lt;code>removeNode(v)&lt;/code>&lt;/li>
&lt;li>Delete edge &lt;code>removeEdge(v,s)&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Drag and zoom functionality is actually implemented through D3, methods as above. If you want to implement right-click menus for clicking nodes or edges, you can use JQ, such as this plugin &lt;a href="https://github.com/swisnl/jQuery-contextMenu" target="_blank" rel="noopener">jQuery-contextMenu&lt;/a>&lt;/p></description></item><item><title>Common Issues in Angular Development</title><link>https://en.1991421.cn/2017/10/26/angular/</link><pubDate>Thu, 26 Oct 2017 20:31:06 +0800</pubDate><guid>https://en.1991421.cn/2017/10/26/angular/</guid><description>&lt;blockquote>
&lt;p>Angular development often encounters various problems. Here I will summarize the main issues I&amp;rsquo;ve faced during development, both for my occasional reference and hopefully to help others solve similar problems.&lt;/p>
&lt;/blockquote>
&lt;p>Note: &lt;code>*This article is continuously updated*&lt;/code>. Due to space limitations, some code snippets are partial. For complete examples, I recommend checking &lt;a href="https://github.com/alanhe421/angular-demo/issues" target="_blank" rel="noopener">GitHub-ISSUES&lt;/a>.&lt;/p>
&lt;h2 id="table-of-contents">
&lt;a class="heading-anchor-link" href="#table-of-contents">Table of Contents&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="table-of-contents"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ol>
&lt;li>&lt;a href="#Can%27t-JavaScript-in-[innerHTML]-be-executed?">Can&amp;rsquo;t JavaScript in [innerHTML] be executed?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Subscribing-to-both-route-parameters-and-query-parameters-%28params-and-queryParams%29">Subscribing to both route parameters and query parameters (params and queryParams)&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Handling-multiple-asynchronous-requests-in-parallel">Handling multiple asynchronous requests in parallel&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Iterating-over-object-properties-with-*ngFor">Iterating over object properties with *ngFor&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Component-class-inheritance">Component class inheritance&lt;/a>&lt;/li>
&lt;li>&lt;a href="#How-to-make-component-styles-extend-beyond-the-component-scope">How to make component styles extend beyond the component scope&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Boolean-type-conversion-for-dropdown-list-options">Boolean type conversion for dropdown list options&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Template-tags-%3cng-container%3e,-%3cng-template%3e">Template tags &lt;ng-container>, &lt;ng-template>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#How-to-dynamically-change-content-in-index.html-when-it%27s-not-templatized-in-CLI">How to dynamically change content in index.html when it&amp;rsquo;s not templatized in CLI&lt;/a>&lt;/li>
&lt;li>&lt;a href="#How-to-add-third-party-CSS-in-CLI-projects">How to add third-party CSS in CLI projects&lt;/a>&lt;/li>
&lt;li>&lt;a href="#Using-interceptors-with-HttpClient">Using interceptors with HttpClient&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="cant-javascript-in-innerhtml-be-executed">
&lt;a class="heading-anchor-link" href="#cant-javascript-in-innerhtml-be-executed">Can&amp;rsquo;t JavaScript in [innerHTML] be executed?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cant-javascript-in-innerhtml-be-executed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Answer: &lt;code>YES&lt;/code>, it&amp;rsquo;s not possible. This involves Angular&amp;rsquo;s security mechanism, which is officially explained &lt;a href="https://angular.io/guide/security" target="_blank" rel="noopener">here&lt;/a>.
The Angular team&amp;rsquo;s position is that if this were supported, it would pose a security risk. Think about it - Angular itself is a JS framework, and if HTML code blocks could execute JS, it would break the framework and introduce critical security vulnerabilities.&lt;/p>
&lt;p>For HTML content, if you want Angular to parse it, you need to inject the DomSanitizer service and mark the HTML as trusted. If commonly used in web applications, consider making it a pipe. See an example &lt;a href="https://github.com/alanhe421/angular-demo/blob/master/src/app/security/safe.pipe.ts" target="_blank" rel="noopener">here&lt;/a>.&lt;/p>
&lt;p>Additionally, using &lt;code>bypassSecurityTrustHtml&lt;/code>, tests show the following behavior:&lt;/p>
&lt;ul>
&lt;li>Inline CSS works normally&lt;/li>
&lt;li>External linked CSS works normally&lt;/li>
&lt;li>JavaScript execution is not allowed&lt;/li>
&lt;li>Embedded media works normally&lt;/li>
&lt;/ul>
&lt;p>If we still want to execute JavaScript in HTML code blocks, we need to take a different approach, like using iframes. There&amp;rsquo;s always a solution.&lt;/p>
&lt;h2 id="subscribing-to-both-route-parameters-and-query-parameters-params-and-queryparams">
&lt;a class="heading-anchor-link" href="#subscribing-to-both-route-parameters-and-query-parameters-params-and-queryparams">Subscribing to both route parameters and query parameters (params and queryParams)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="subscribing-to-both-route-parameters-and-query-parameters-params-and-queryparams"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>When entering a view page, we often need to subscribe to parameters. There are various types of parameters: path parameters, query parameters, and anchor parameters. If we write using nested subscriptions, it would look like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">route&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">queryParams&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">qparams&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">qparams&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;title&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">route&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;id&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But such code is extremely difficult to maintain. RXJS has a solution for this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">obsCombined&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">combineLatest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">route&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">route&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">queryParams&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">qparams&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">params&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">qparams&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">obsCombined&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ap&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ap&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ap&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;params&amp;#39;&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">qparams&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">ap&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;qparams&amp;#39;&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">qparams&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;title&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;id&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This reduces nesting and makes the code much cleaner.&lt;/p>
&lt;p>Note a detail: &lt;code>import {Observable} from &amp;quot;rxjs/Rx&amp;quot;;&lt;/code>
Writing &lt;code>import {Observable} from &amp;quot;rxjs/Observable&amp;quot;;&lt;/code> will result in the error &lt;code>Property 'combineLatest' does not exist on type 'typeof Observable'.&lt;/code>&lt;/p>
&lt;h2 id="iterating-over-object-properties-with-ngfor">
&lt;a class="heading-anchor-link" href="#iterating-over-object-properties-with-ngfor">Iterating over object properties with *ngFor&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="iterating-over-object-properties-with-ngfor"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes you need to iterate over object properties, but Angular&amp;rsquo;s ngFor directive only supports iterating over arrays. To convert objects, consider encapsulating a pipe as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">Pipe&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">PipeTransform&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;@angular/core&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Object to array
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@Pipe&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;keys&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">KeysPipe&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">PipeTransform&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">transform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">args?&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kt">any&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">Object&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">keys&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This essentially uses JavaScript&amp;rsquo;s &lt;code>Object.keys()&lt;/code> method, which:&lt;/p>
&lt;blockquote>
&lt;/blockquote>
&lt;p>The Object.keys() method returns an array of a given object&amp;rsquo;s own enumerable properties, in the same order as that provided by a for&amp;hellip;in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).&lt;/p>
&lt;p>After creating the pipe, use it like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="err">*&lt;/span>&lt;span class="na">ngFor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;let key of facetFields|keys&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">key:{{key}},
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">value:{{facetFields[key]}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="handling-multiple-asynchronous-requests-in-parallel">
&lt;a class="heading-anchor-link" href="#handling-multiple-asynchronous-requests-in-parallel">Handling multiple asynchronous requests in parallel&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="handling-multiple-asynchronous-requests-in-parallel"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Sometimes you need to process multiple request results simultaneously. If done with nesting, it would look like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/test1&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res1&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/test2&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res2&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">res1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">res2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This structure is cumbersome and hard to maintain. Using RxJS&amp;rsquo;s &lt;code>forkJoin&lt;/code> operator:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">req1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/test1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">req2&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">http&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/api/test2&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Rx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Observable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forkJoin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">req2&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">res&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">res1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">res2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This only notifies us when all request results are received.&lt;/p>
&lt;h2 id="component-class-inheritance">
&lt;a class="heading-anchor-link" href="#component-class-inheritance">Component class inheritance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="component-class-inheritance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Support for component inheritance was added in &lt;code>ng-v2.3&lt;/code>, further improving code reusability.&lt;/p>
&lt;p>Component inheritance supports:&lt;/p>
&lt;ul>
&lt;li>Meta annotations, like @input, @output&lt;/li>
&lt;li>Constructor&lt;/li>
&lt;li>Component lifecycle hooks&lt;/li>
&lt;/ul>
&lt;p>All three support reuse. See the official explanation &lt;a href="https://github.com/angular/angular/commit/f5c8e09" target="_blank" rel="noopener">here&lt;/a>
&lt;figure class="image-figure">
&lt;img
src="https://user-images.githubusercontent.com/9245110/32786687-9d9776ee-c98f-11e7-972e-79e47e713e9f.png"
alt="image"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="how-to-make-component-styles-extend-beyond-the-component-scope">
&lt;a class="heading-anchor-link" href="#how-to-make-component-styles-extend-beyond-the-component-scope">How to make component styles extend beyond the component scope&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-make-component-styles-extend-beyond-the-component-scope"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="how-to-make-component-as-styles-available-in-its-child-component-b">
&lt;a class="heading-anchor-link" href="#how-to-make-component-as-styles-available-in-its-child-component-b">How to make component A&amp;rsquo;s styles available in its child component B&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-make-component-as-styles-available-in-its-child-component-b"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Besides writing styles as external CSS (global CSS), you can modify the view encapsulation mode to allow component styles to extend outward for use by other components. Just configure component A&amp;rsquo;s view mode like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">@Component({
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> selector: &amp;#39;app-css&amp;#39;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> templateUrl: &amp;#39;./css.component.html&amp;#39;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> styleUrls: [&amp;#39;./css.component.css&amp;#39;],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> encapsulation: ViewEncapsulation.None
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">})
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With this configuration, all DOM elements under this component at any level can use these styles.&lt;/p>
&lt;h3 id="note">
&lt;a class="heading-anchor-link" href="#note">Note&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="note"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>Component inheritance does not support templates and styles; any behaviors that manipulate the DOM should be physically separated.&lt;/code>&lt;/p>
&lt;p>&lt;a href="https://github.com/alanhe421/angular-demo/issues/10" target="_blank" rel="noopener">See a practical demo here&lt;/a>&lt;/p>
&lt;h2 id="boolean-type-conversion-for-dropdown-list-options">
&lt;a class="heading-anchor-link" href="#boolean-type-conversion-for-dropdown-list-options">Boolean type conversion for dropdown list options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="boolean-type-conversion-for-dropdown-list-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>When we use ngModel two-way binding on dropdown lists, if our object is not a string type (e.g., boolean), it will be converted to a string when a user selects an option.&lt;/p>
&lt;/blockquote>
&lt;p>If non-string types are provided for dropdown options, the default value will be converted to a string. Using ngValue preserves the original type:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">select&lt;/span> &lt;span class="err">[(&lt;/span>&lt;span class="na">ngModel&lt;/span>&lt;span class="err">)]=&amp;#34;&lt;/span>&lt;span class="na">isList&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">(&lt;/span>&lt;span class="na">ngModelChange&lt;/span>&lt;span class="err">)=&amp;#34;&lt;/span>&lt;span class="na">viewTypeChanged&lt;/span>&lt;span class="err">()&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span> &lt;span class="err">[&lt;/span>&lt;span class="na">ngValue&lt;/span>&lt;span class="err">]=&amp;#34;&lt;/span>&lt;span class="na">true&lt;/span>&lt;span class="err">&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>List View&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">option&lt;/span> &lt;span class="err">[&lt;/span>&lt;span class="na">ngValue&lt;/span>&lt;span class="err">]=&amp;#34;&lt;/span>&lt;span class="na">false&lt;/span>&lt;span class="err">&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Group View&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">option&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">select&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="template-tags-ng-container-ng-template">
&lt;a class="heading-anchor-link" href="#template-tags-ng-container-ng-template">Template tags &lt;ng-container>, &lt;ng-template>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="template-tags-ng-container-ng-template"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We often use &lt;code>ng-template&lt;/code> with directives like ngIf:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="err">*&lt;/span>&lt;span class="na">ngIf&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;isList;else elseBlock&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span> &lt;span class="err">*&lt;/span>&lt;span class="na">ngFor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;let item of items&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">routerLink&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;../detail/{{item.id}}&amp;#39;&lt;/span> &lt;span class="err">[&lt;/span>&lt;span class="na">queryParams&lt;/span>&lt;span class="err">]=&amp;#34;{&lt;/span>&lt;span class="na">title:item&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">title&lt;/span>&lt;span class="err">}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{item.title}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ng-template&lt;/span> &lt;span class="err">#&lt;/span>&lt;span class="na">elseBlock&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Group View
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ng-template&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>However, this approach adds an extra &lt;code>div&lt;/code> tag for the if statement, which we might not want. To avoid adding this extra &lt;code>div&lt;/code> tag, use the ng-container tag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ng-container&lt;/span> &lt;span class="err">*&lt;/span>&lt;span class="na">ngIf&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;isList;else elseBlock&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">li&lt;/span> &lt;span class="err">*&lt;/span>&lt;span class="na">ngFor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;let item of items&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">a&lt;/span> &lt;span class="na">routerLink&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;../detail/{{item.id}}&amp;#39;&lt;/span> &lt;span class="err">[&lt;/span>&lt;span class="na">queryParams&lt;/span>&lt;span class="err">]=&amp;#34;{&lt;/span>&lt;span class="na">title:item&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">title&lt;/span>&lt;span class="err">}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> {{item.title}}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">a&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">li&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ul&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ng-container&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">ng-template&lt;/span> &lt;span class="err">#&lt;/span>&lt;span class="na">elseBlock&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Group View
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="err">*&lt;/span>&lt;span class="na">ngFor&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;let i of [1,2,3]&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> {{i}}&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h3&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">ng-template&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="how-to-dynamically-change-content-in-indexhtml-when-its-not-templatized-in-cli">
&lt;a class="heading-anchor-link" href="#how-to-dynamically-change-content-in-indexhtml-when-its-not-templatized-in-cli">How to dynamically change content in index.html when it&amp;rsquo;s not templatized in CLI&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-dynamically-change-content-in-indexhtml-when-its-not-templatized-in-cli"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>In Angular development, most people use the official CLI build tool, but under the official CLI build, the index page is static. For example, if you want to add content to the index page during the build and packaging process, you&amp;rsquo;ll have to implement it manually.&lt;/p>
&lt;/blockquote>
&lt;p>Here&amp;rsquo;s a scenario and solution:&lt;/p>
&lt;h3 id="scenario">
&lt;a class="heading-anchor-link" href="#scenario">Scenario&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scenario"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>For production deployment, I want to add &lt;meta name="releaseDate" content="${releaseDate}"/> to the built and packaged index.html page, so by looking at the source code marker, I can know when this new frontend version was released.&lt;/p>
&lt;/blockquote>
&lt;h3 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h4 id="add-a-script-file">Add a script file&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">fs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;fs&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">cheerio&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;cheerio&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">moment&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;moment&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">indexFilePath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;dist/index.html&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">readFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">indexFilePath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;utf8&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">$&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">cheerio&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">decodeEntities&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">releaseDate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">moment&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;YYYY-MM-DD HH:mm&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">$&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;meta&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">last&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`&amp;lt;meta name=&amp;#34;releaseDate&amp;#34; content=&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">releaseDate&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">&amp;#34;/&amp;gt;`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// now write that file back
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">writeFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">indexFilePath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">$&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">html&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Successfully rewrote index html&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="packagejson">package.json&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;build:prod&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;ng build --prod &amp;amp;&amp;amp; node scripts/after-build.js&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This way, when we run &lt;code>npm run build:prod&lt;/code>, it will execute the content modification script after the frontend build is successful.&lt;/p>
&lt;h2 id="how-to-add-third-party-css-in-cli-projects">
&lt;a class="heading-anchor-link" href="#how-to-add-third-party-css-in-cli-projects">How to add third-party CSS in CLI projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-add-third-party-css-in-cli-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>For third-party CSS, considering future upgrades and avoiding direct tampering with source code by developers, it&amp;rsquo;s generally not placed under assets.&lt;/p>
&lt;/blockquote>
&lt;p>For third-party CSS:&lt;/p>
&lt;h3 id="if-you-want-to-package-it-directly-into-stylescss-there-are-two-approaches">
&lt;a class="heading-anchor-link" href="#if-you-want-to-package-it-directly-into-stylescss-there-are-two-approaches">If you want to package it directly into styles.css, there are two approaches:&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="if-you-want-to-package-it-directly-into-stylescss-there-are-two-approaches"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Configure it in the JSON configuration file&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;styles&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;../node_modules/ngx-bootstrap/datepicker/bs-datepicker.css&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;styles.css&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Import directly in CSS&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* You can add global styles to this file, and also import other style files */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">import&lt;/span> &lt;span class="s1">&amp;#39;~ngx-bootstrap/datepicker/bs-datepicker.css&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Between these two options, I recommend the first one. For CLI projects, prioritize using the CLI approach.&lt;/p>
&lt;h3 id="if-you-still-want-physical-separation-simply-import-in-indexhtml">
&lt;a class="heading-anchor-link" href="#if-you-still-want-physical-separation-simply-import-in-indexhtml">If you still want physical separation, simply import in index.html&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="if-you-still-want-physical-separation-simply-import-in-indexhtml"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="using-interceptors-with-httpclient">
&lt;a class="heading-anchor-link" href="#using-interceptors-with-httpclient">Using interceptors with HttpClient&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-interceptors-with-httpclient"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Angular 4.3 introduced interceptor functionality, which supports modifying requests and responses - meaning you can pre-process before sending a request and after receiving a response.&lt;/p>
&lt;p>Official description:&lt;/p>
&lt;blockquote>
&lt;p>A major feature of /http is interception, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.&lt;/p>
&lt;/blockquote>
&lt;h3 id="scenario-1">
&lt;a class="heading-anchor-link" href="#scenario-1">Scenario&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scenario-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>In actual development for user authentication, my frontend needs to add a token in the request header, and when receiving a backend response, execute corresponding actions for specific states.&lt;/p>
&lt;/blockquote>
&lt;h3 id="code">
&lt;a class="heading-anchor-link" href="#code">CODE&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>token.interceptor.service.ts&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">Injectable&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@angular/core&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">AuthService&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./auth.service&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">HttpErrorResponse&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HttpEvent&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HttpHandler&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HttpInterceptor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">HttpRequest&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@angular/common/http&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">Observable&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;rxjs/Observable&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nx">AlertService&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./alert.service&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="s1">&amp;#39;rxjs/add/operator/do&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Created by He on 10/01/18.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Token Interceptor
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@Injectable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">TokenInterceptor&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">HttpInterceptor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">public&lt;/span> &lt;span class="nx">authService&lt;/span>: &lt;span class="kt">AuthService&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">private&lt;/span> &lt;span class="nx">alertService&lt;/span>: &lt;span class="kt">AlertService&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">intercept&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>: &lt;span class="kt">HttpRequest&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">any&lt;/span>&lt;span class="p">&amp;gt;,&lt;/span> &lt;span class="nx">next&lt;/span>: &lt;span class="kt">HttpHandler&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">HttpEvent&lt;/span>&lt;span class="err">&amp;lt;&lt;/span>&lt;span class="na">any&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getToken&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">clone&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">setHeaders&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Authorization&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="sb">`Bearer &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getToken&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">next&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">handle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">do&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span> &lt;span class="k">instanceof&lt;/span> &lt;span class="nx">HttpErrorResponse&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="mi">401&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">alertService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">showAlert&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;danger&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">msg&lt;/span>: &lt;span class="kt">err.error&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;err&amp;#39;&lt;/span>&lt;span class="p">]});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">removeToken&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">reload&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">Observable&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="out-of-memory-during-build">
&lt;a class="heading-anchor-link" href="#out-of-memory-during-build">Out of Memory During Build&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="out-of-memory-during-build"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Solution: specify NODE memory&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;node --max-old-space-size=8192 $(which ng) build&amp;#34;&lt;/span>&lt;span class="err">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="still-have-questions-not-detailed-enough">
&lt;a class="heading-anchor-link" href="#still-have-questions-not-detailed-enough">Still have questions??? Not detailed enough???&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="still-have-questions-not-detailed-enough"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-26-question_72px_1094871_easyicon.net.png"
alt="Still have questions???"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="welcome-to-submit-tickets-and-check-the-latest-answers-to-the-above-questions-on-githubangular-demo-click-herehttpsgithubcomalanhe421angular-demoissues">
&lt;a class="heading-anchor-link" href="#welcome-to-submit-tickets-and-check-the-latest-answers-to-the-above-questions-on-githubangular-demo-click-herehttpsgithubcomalanhe421angular-demoissues">Welcome to submit tickets and check the latest answers to the above questions on GitHub/angular-demo &lt;a href="https://github.com/alanhe421/angular-demo/issues" target="_blank" rel="noopener">Click here&lt;/a>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="welcome-to-submit-tickets-and-check-the-latest-answers-to-the-above-questions-on-githubangular-demo-click-herehttpsgithubcomalanhe421angular-demoissues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3></description></item><item><title>My Roadmap for Learning Angular</title><link>https://en.1991421.cn/2017/10/23/angular/</link><pubDate>Mon, 23 Oct 2017 21:21:20 +0800</pubDate><guid>https://en.1991421.cn/2017/10/23/angular/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-23-angular.png"
alt="Angular Logo"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>When Google launched the &amp;ldquo;new&amp;rdquo; Angular (formerly Angular 2+), the naming convention caused some initial confusion. To clarify: &amp;ldquo;AngularJS&amp;rdquo; refers strictly to the legacy 1.x framework. Modern Angular supports TypeScript, JavaScript, and Dart—though TypeScript is the industry standard and my personal choice.&lt;/p>
&lt;/blockquote>
&lt;p>Here is the learning path I recommend for anyone starting with modern Angular:&lt;/p>
&lt;h3 id="1-start-with-the-why">
&lt;a class="heading-anchor-link" href="#1-start-with-the-why">1. Start with the &amp;ldquo;Why&amp;rdquo;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-start-with-the-why"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Before diving into code, read the introductory sections of the official documentation. Understand what problems Angular is designed to solve (e.g., enterprise-scale SPA development) and how it integrates with TypeScript.&lt;/p>
&lt;h3 id="2-learn-the-language-typescript">
&lt;a class="heading-anchor-link" href="#2-learn-the-language-typescript">2. Learn the Language: TypeScript&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-learn-the-language-typescript"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Since Angular is built on TypeScript, familiarity with the language is non-negotiable. Skim the &lt;a href="https://www.typescriptlang.org/docs/handbook/intro.html" target="_blank" rel="noopener">TypeScript Handbook&lt;/a>. Focus on its object-oriented features, interfaces, and how it acts as a superset of JavaScript.&lt;/p>
&lt;h3 id="3-the-tour-of-heroes-tutorial">
&lt;a class="heading-anchor-link" href="#3-the-tour-of-heroes-tutorial">3. The &amp;ldquo;Tour of Heroes&amp;rdquo; Tutorial&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="3-the-tour-of-heroes-tutorial"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The &lt;a href="https://angular.io/tutorial" target="_blank" rel="noopener">official tutorial&lt;/a> is excellent. It walks you through building a real application, covering routing, services, and data binding in a logical flow.&lt;/p>
&lt;h3 id="4-fundamentals-and-beyond">
&lt;a class="heading-anchor-link" href="#4-fundamentals-and-beyond">4. Fundamentals and Beyond&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="4-fundamentals-and-beyond"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Once you&amp;rsquo;ve finished the tutorial, dive deeper into the &amp;ldquo;Fundamentals&amp;rdquo; and &amp;ldquo;Advanced&amp;rdquo; sections of the documentation. After completing the basics, you&amp;rsquo;ll have enough knowledge to start contributing to real-world projects.&lt;/p>
&lt;h3 id="5-internalize-the-core-pillars">
&lt;a class="heading-anchor-link" href="#5-internalize-the-core-pillars">5. Internalize the Core Pillars&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="5-internalize-the-core-pillars"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Keep these five concepts in your mind as you build:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Modules&lt;/strong>: Organization and lazy loading.&lt;/li>
&lt;li>&lt;strong>Components&lt;/strong>: The building blocks of your UI.&lt;/li>
&lt;li>&lt;strong>Pipes&lt;/strong>: Data transformation.&lt;/li>
&lt;li>&lt;strong>Directives&lt;/strong>: Adding behavior to the DOM.&lt;/li>
&lt;li>&lt;strong>Services&lt;/strong>: Handling business logic and data sharing.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="pro-tip-revisit-the-docs">
&lt;a class="heading-anchor-link" href="#pro-tip-revisit-the-docs">Pro-Tip: Revisit the Docs&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pro-tip-revisit-the-docs"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official documentation isn&amp;rsquo;t a &amp;ldquo;one-and-done&amp;rdquo; read. It evolves with the framework, and your understanding will deepen as you gain more experience. Revisit it whenever you face a complex architectural decision—it remains the most authoritative source.&lt;/p>
&lt;h2 id="recommended-resources">
&lt;a class="heading-anchor-link" href="#recommended-resources">Recommended Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="websites">
&lt;a class="heading-anchor-link" href="#websites">Websites&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="websites"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Angular Official&lt;/strong>: &lt;a href="https://angular.io" target="_blank" rel="noopener">angular.io&lt;/a>&lt;/li>
&lt;li>&lt;strong>TypeScript Official&lt;/strong>: &lt;a href="https://www.typescriptlang.org/" target="_blank" rel="noopener">typescriptlang.org&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="books">
&lt;a class="heading-anchor-link" href="#books">Books&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="books"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;a href="https://www.ng-book.com/2/" target="_blank" rel="noopener">&lt;strong>ng-book&lt;/strong>&lt;/a>: Often cited as the &amp;ldquo;In-depth Bible&amp;rdquo; for Angular.&lt;/li>
&lt;li>&lt;a href="http://www.ituring.com.cn/book/1874" target="_blank" rel="noopener">&lt;strong>Angular Authoritative Tutorial (Chinese Translation)&lt;/strong>&lt;/a>: A great alternative if you prefer reading in Chinese.&lt;/li>
&lt;/ul>
&lt;h3 id="community-projects--demos">
&lt;a class="heading-anchor-link" href="#community-projects--demos">Community Projects &amp;amp; Demos&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="community-projects--demos"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;a href="https://github.com/AngularClass/angular-starter" target="_blank" rel="noopener">&lt;strong>angular-starter&lt;/strong>&lt;/a>: A great boilerplate for project structure.&lt;/li>
&lt;li>&lt;a href="https://github.com/akveo/ngx-admin" target="_blank" rel="noopener">&lt;strong>ngx-admin&lt;/strong>&lt;/a>: A sophisticated admin dashboard template that shows what Angular is capable of.&lt;/li>
&lt;li>&lt;a href="https://github.com/damoqiongqiu/NiceFish" target="_blank" rel="noopener">&lt;strong>NiceFish&lt;/strong>&lt;/a>: A well-known Chinese community project for learning Angular.&lt;/li>
&lt;/ul>
&lt;h3 id="faqs--troubleshooting">
&lt;a class="heading-anchor-link" href="#faqs--troubleshooting">FAQs &amp;amp; Troubleshooting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="faqs--troubleshooting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;a href="https://en.1991421.cn/2017/10/26/dc22bcd7/" target="_blank" rel="noopener">&lt;strong>Angular Common Issues (My Collection)&lt;/strong>&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Fix "JavaScript heap out of memory" During Build</title><link>https://en.1991421.cn/2017/10/11/javascript-heap-out-of-memory/</link><pubDate>Wed, 11 Oct 2017 20:56:20 +0800</pubDate><guid>https://en.1991421.cn/2017/10/11/javascript-heap-out-of-memory/</guid><description>&lt;blockquote>
&lt;p>While building an Angular project with webpack, I frequently hit &lt;code>JavaScript heap out of memory&lt;/code> (V8 OOM). After some digging, here’s the fix.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-12-044030.jpg"
alt="error"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The build’s memory usage exceeds V8’s default heap size. Raise the limit.&lt;/p>
&lt;h3 id="1-in-your-build-scripts">
&lt;a class="heading-anchor-link" href="#1-in-your-build-scripts">1) In your build scripts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="1-in-your-build-scripts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add &lt;code>node --max_old_space_size=4096&lt;/code>. Example below.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-11-151309.jpg"
alt="package.json"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-11-151334.jpg"
alt="package.json"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="2-dont-patch-binaries-in-node_modules">
&lt;a class="heading-anchor-link" href="#2-dont-patch-binaries-in-node_modules">2) Don’t patch binaries in node_modules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="2-dont-patch-binaries-in-node_modules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Some posts suggest editing &lt;code>node_modules/.bin/webpack&lt;/code>. It works but is not recommended — node_modules is third‑party code. Reinstalls or upgrades will wipe your change and it harms collaboration.&lt;/p>
&lt;p>If you do patch the .bin script, it looks like this:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-11-151428.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Use the build‑script approach instead of editing binaries.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;blockquote>
&lt;p>Currently, by default v8 has a memory limit of 512mb on 32-bit systems, and 1gb on 64-bit systems. The limit can be raised by setting –max-old-space-size to a maximum of ~1gb (32-bit) and ~1.7gb (64-bit), but it is recommended that you split your single process into several workers if you are hitting memory limits.&lt;/p>
&lt;/blockquote>
&lt;p>This explains V8’s default heap limits and why increasing &lt;code>--max_old_space_size&lt;/code> fixes the issue.&lt;/p></description></item><item><title>Cordova Development Common Issues</title><link>https://en.1991421.cn/2017/10/09/cordova/</link><pubDate>Mon, 09 Oct 2017 21:55:12 +0800</pubDate><guid>https://en.1991421.cn/2017/10/09/cordova/</guid><description>&lt;blockquote>
&lt;p>When developing with ionic or directly using Cordova, you often encounter some problems. Here&amp;rsquo;s a summary, &lt;code>this article is continuously updated&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;a href="#codename-mark-and-build-number-relationship">Android Version: Codename, Mark, and Build Number Relationship&lt;/a>&lt;/li>
&lt;li>&lt;a href="#is-crosswalk-webview-needed">Is crosswalk-webview needed?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#config-xml-file-modification-need-to-re-add-platform">config.xml file modification, need to re-add platform?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#missing-info-plist-key">iOS - Photo Library Permissions&lt;/a>&lt;/li>
&lt;li>&lt;a href="#ios-build-upload-successful-but-not-showing-in-versions">iOS - Build upload successful but not showing in versions&lt;/a>&lt;/li>
&lt;li>&lt;a href="#gradle">Could not find an installed version of Gradle&lt;/a>&lt;/li>
&lt;li>&lt;a href="#">com.android.ide.common.process.ProcessException: Failed to execute aapt&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="codename-mark-and-build-number-relationship">
&lt;a class="heading-anchor-link" href="#codename-mark-and-build-number-relationship">Codename, Mark and Build Number Relationship&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="codename-mark-and-build-number-relationship"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Codename&lt;/th>
&lt;th style="text-align: left">Version&lt;/th>
&lt;th style="text-align: left">API Level&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Nougat&lt;/td>
&lt;td style="text-align: left">7.1&lt;/td>
&lt;td style="text-align: left">API Level 25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Nougat&lt;/td>
&lt;td style="text-align: left">7.0&lt;/td>
&lt;td style="text-align: left">API Level 24&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Marshmallow&lt;/td>
&lt;td style="text-align: left">6.0&lt;/td>
&lt;td style="text-align: left">API Level 23&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Lollipop&lt;/td>
&lt;td style="text-align: left">5.1&lt;/td>
&lt;td style="text-align: left">API Level 22&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Lollipop&lt;/td>
&lt;td style="text-align: left">5.0&lt;/td>
&lt;td style="text-align: left">API Level 21&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">KitKat&lt;/td>
&lt;td style="text-align: left">4.4-4.4.4&lt;/td>
&lt;td style="text-align: left">API Level 19&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Jelly Bean&lt;/td>
&lt;td style="text-align: left">4.3.x&lt;/td>
&lt;td style="text-align: left">API Level 18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Jelly Bean&lt;/td>
&lt;td style="text-align: left">4.2.x&lt;/td>
&lt;td style="text-align: left">API Level 17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Jelly Bean&lt;/td>
&lt;td style="text-align: left">4.1.x&lt;/td>
&lt;td style="text-align: left">API Level 16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Ice Cream Sandwich&lt;/td>
&lt;td style="text-align: left">4.0.3-4.0.4&lt;/td>
&lt;td style="text-align: left">API Level 15, NDK 8&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For the latest and most complete information, &lt;a href="https://source.android.com/source/build-numbers" target="_blank" rel="noopener">click to view official documentation&lt;/a>&lt;/p>
&lt;h2 id="is-crosswalk-webview-needed">
&lt;a class="heading-anchor-link" href="#is-crosswalk-webview-needed">Is crosswalk-webview needed?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-crosswalk-webview-needed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview" target="_blank" rel="noopener">cordova-plugin-crosswalk-webview&lt;/a>&lt;/p>
&lt;p>This plugin mainly solves performance issues on older phones with laggy APP operations. However, if you don&amp;rsquo;t need to support very old phones (Android 4.4 and below), it&amp;rsquo;s not recommended to install.&lt;/p>
&lt;blockquote>
&lt;p>Before Android 4.4, the Android system browser kernel was WebKit. Android 4.4 switched the system browser to Chromium (kernel is Blink, a fork of Webkit).&lt;/p>
&lt;/blockquote>
&lt;p>Installing this plugin means using &lt;a href="http://blog.csdn.net/itcatface/article/details/49799337" target="_blank" rel="noopener">Crosswalk Webview&lt;/a> to replace the system&amp;rsquo;s WebView.
Essentially, it brings &lt;code>Chromium&lt;/code> into the APP, but Android 4.4 and later have already switched to &lt;code>Chromium&lt;/code>.&lt;/p>
&lt;p>Advantages:&lt;/p>
&lt;ul>
&lt;li>Not dependent on Android version&lt;/li>
&lt;li>Compatibility&lt;/li>
&lt;li>Performance improvement compared to old system webviews&lt;/li>
&lt;/ul>
&lt;p>Disadvantages:&lt;/p>
&lt;ul>
&lt;li>Increases memory usage&lt;/li>
&lt;li>Increases APK size (about 17MB)&lt;/li>
&lt;li>Increases installation size (about 50MB)&lt;/li>
&lt;/ul>
&lt;h2 id="configxml-file-modification-need-to-re-add-platform">
&lt;a class="heading-anchor-link" href="#configxml-file-modification-need-to-re-add-platform">config.xml file modification, need to re-add platform?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configxml-file-modification-need-to-re-add-platform"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>For ionic projects, &lt;code>not needed&lt;/code>. Execute &lt;code>ionic cordova build platform&lt;/code> or &lt;code>ionic cordova run platform&lt;/code>, ionic-cli directly updates the corresponding code in the platform.&lt;/li>
&lt;li>For Cordova initialized projects, you need to rm and then add.&lt;/li>
&lt;/ul>
&lt;h2 id="ios---photo-library-permissions">
&lt;a class="heading-anchor-link" href="#ios---photo-library-permissions">iOS - Photo Library Permissions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ios---photo-library-permissions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the APP doesn&amp;rsquo;t declare camera permissions, during actual APP operation, accessing the photo library will cause the APP to crash directly with an error. So you must add the corresponding plugin, specific configuration information is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">edit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">config&lt;/span> &lt;span class="n">target&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;NSCameraUsageDescription&amp;#34;&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;*-Info.plist&amp;#34;&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;merge&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="n">need&lt;/span> &lt;span class="n">camera&lt;/span> &lt;span class="n">access&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">take&lt;/span> &lt;span class="n">pictures&lt;/span>&lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">edit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">edit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">config&lt;/span> &lt;span class="n">target&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;NSMicrophoneUsageDescription&amp;#34;&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;*-Info.plist&amp;#34;&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;merge&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="n">need&lt;/span> &lt;span class="n">microphone&lt;/span> &lt;span class="n">access&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">record&lt;/span> &lt;span class="n">sounds&lt;/span>&lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">edit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">edit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">config&lt;/span> &lt;span class="n">target&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;NSPhotoLibraryUsageDescription&amp;#34;&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;*-Info.plist&amp;#34;&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;merge&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="n">need&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">photo&lt;/span> &lt;span class="n">library&lt;/span> &lt;span class="n">access&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">get&lt;/span> &lt;span class="n">pictures&lt;/span> &lt;span class="n">from&lt;/span> &lt;span class="n">there&lt;/span>&lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">edit&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cordova-plugin-media-capture&amp;#34;&lt;/span> &lt;span class="n">spec&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;~1.4.3&amp;#34;&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">variable&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;CAMERA_USAGE_DESCRIPTION&amp;#34;&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;App would like to access the camera.&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">variable&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;MICROPHONE_USAGE_DESCRIPTION&amp;#34;&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;App would like to access the microphone.&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">variable&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;PHOTOLIBRARY_USAGE_DESCRIPTION&amp;#34;&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;App would like to access the library.&amp;#34;&lt;/span>&lt;span class="o">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;/&lt;/span>&lt;span class="n">plugin&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the content in the &lt;code>edit-config&lt;/code> tags will be packaged into the info.plist file. This section must be written, otherwise the final packaged APP submission will fail.&lt;/p>
&lt;h2 id="ios---build-upload-successful-but-not-showing-in-versions">
&lt;a class="heading-anchor-link" href="#ios---build-upload-successful-but-not-showing-in-versions">iOS - Build upload successful but not showing in versions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ios---build-upload-successful-but-not-showing-in-versions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>If the build upload shows success, but you don&amp;rsquo;t see it in iTunes Connect -&amp;gt; Activity -&amp;gt; All Build Versions, it means the submitted APP was rejected. There will be an email explaining the error information.
For example, the following information:&lt;/p>
&lt;blockquote>
&lt;p>Missing Info.plist key - This app attempts to access privacy-sensitive data without a usage description. The app&amp;rsquo;s Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.
Once these issues have been corrected, you can then redeliver the corrected binary.&lt;/p>
&lt;/blockquote>
&lt;p>After fixing accordingly, submit again, and simply refresh the page to see the submitted version and its status.&lt;/p>
&lt;h2 id="span-idgradlecould-not-find-an-installed-version-of-gradlespan">
&lt;a class="heading-anchor-link" href="#span-idgradlecould-not-find-an-installed-version-of-gradlespan">&lt;span id="gradle">Could not find an installed version of Gradle&lt;/span>&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="span-idgradlecould-not-find-an-installed-version-of-gradlespan"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The complete error is:&lt;/p>
&lt;blockquote>
&lt;p>Could not find an installed version of Gradle either in Android Studio, or on your system to install the gradle wrapper. Please include gradle in your path, or install Android Studio&lt;/p>
&lt;/blockquote>
&lt;p>The official website has relevant introduction as follows:&lt;/p>
&lt;blockquote>
&lt;p>As of &lt;a href="mailto:cordova-android@4.0.0">cordova-android@4.0.0&lt;/a>, Cordova for Android projects are built using Gradle.&lt;/p>
&lt;/blockquote>
&lt;p>So the problem is understood. The solution is exactly as the error suggests - install Gradle.&lt;/p>
&lt;p>If using &lt;code>mac&lt;/code>, it&amp;rsquo;s recommended to execute &lt;code>brew install gradle&lt;/code>.&lt;/p>
&lt;h2 id="comandroididecommonprocessprocessexception-failed-to-execute-aapt">
&lt;a class="heading-anchor-link" href="#comandroididecommonprocessprocessexception-failed-to-execute-aapt">com.android.ide.common.process.ProcessException: Failed to execute aapt&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="comandroididecommonprocessprocessexception-failed-to-execute-aapt"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Executing &lt;code>cordova build android&lt;/code> will report this error, caused by crosswalk-webview in the project. Solutions:&lt;/p>
&lt;ol>
&lt;li>Uninstall cordova-plugin-crosswalk-webview plugin
&lt;code>cordova plugin rm cordova-plugin-crosswalk-webview&lt;/code>&lt;/li>
&lt;li>Install cordova-android-support-gradle-release plugin
&lt;code>cordova plugin add cordova-android-support-gradle-release&lt;/code>
Both methods work, depending on whether we still want to use crosswalk. For its advantages and disadvantages, &lt;a href="https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview" target="_blank" rel="noopener">click here&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>mysql error: Invalid default value for date</title><link>https://en.1991421.cn/2017/10/04/mysql-error-invalid-default-value-for-date/</link><pubDate>Wed, 04 Oct 2017 03:34:51 +0800</pubDate><guid>https://en.1991421.cn/2017/10/04/mysql-error-invalid-default-value-for-date/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-10-03-201336.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>Recently encountered this error during MySQL data migration. I inferred it was caused by MySQL version differences - my A and B machines have MySQL versions 5.6 and 5.7 respectively.&lt;/p>
&lt;/blockquote>
&lt;h2 id="cause">
&lt;a class="heading-anchor-link" href="#cause">Cause&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cause"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Googled it and found it was actually a MySQL SQL Mode issue.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Modify configuration file, restart service&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sql_mode=ONLY_FULL_GROUP_BY,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: Removing the sql_mode configuration won&amp;rsquo;t work&lt;/p></description></item><item><title>Analyzing Alibaba Cloud CDN 403 Errors</title><link>https://en.1991421.cn/2017/09/25/cdn403/</link><pubDate>Mon, 25 Sep 2017 22:45:47 +0800</pubDate><guid>https://en.1991421.cn/2017/09/25/cdn403/</guid><description>&lt;blockquote>
&lt;p>To improve web performance, CDN is a standard service for accelerating access. Our site uses Angular and is relatively heavy on the frontend, so we adopted Alibaba Cloud CDN. However, we occasionally saw 403s — frustrating, since we use a CDN for speed; if it returns 403, what’s the point?&lt;/p>
&lt;/blockquote>
&lt;h2 id="concepts">
&lt;a class="heading-anchor-link" href="#concepts">Concepts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="concepts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="oss">
&lt;a class="heading-anchor-link" href="#oss">OSS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="oss"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Alibaba Cloud Object Storage Service (OSS) is a massive, secure, low-cost, highly reliable object storage with platform‑agnostic REST APIs and 11 nines durability. It’s suitable for websites and developers to store and access any data, anywhere.&lt;/p>
&lt;h3 id="cdn">
&lt;a class="heading-anchor-link" href="#cdn">CDN&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cdn"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Alibaba Cloud CDN is a distributed network of edge nodes layered over backbone networks, replacing the traditional single web‑server delivery model. Content is published to edge nodes and a scheduler routes user requests to the best node for lower latency and faster delivery.&lt;/p>
&lt;h2 id="why-403-happens">
&lt;a class="heading-anchor-link" href="#why-403-happens">Why 403 happens&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-403-happens"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-25-145738.jpg"
alt="403-error"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Two details: 403 means unauthorized; the &lt;code>X-Swift-Error&lt;/code> says &lt;code>orig response 4XX error&lt;/code>.&lt;/p>
&lt;p>We saw 403 when requesting a resource via CDN, even though the object existed in OSS. The missing link is understanding CDN vs. OSS.&lt;/p>
&lt;p>When the browser requests a page, the CDN fetches resources. If an object is not cached at the edge, the CDN pulls from origin (OSS) using the client’s Referer header. If CDN allows your site (whitelisted Referer) but OSS hotlink protection does not, OSS will return 403 and the CDN forwards it.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-25-150102.jpg"
alt="browser&amp;#43;cdn&amp;#43;oss"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="fix">
&lt;a class="heading-anchor-link" href="#fix">Fix&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fix"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="option-1">
&lt;a class="heading-anchor-link" href="#option-1">Option 1&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="option-1"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Add &lt;code>a.alanhe.me&lt;/code> to OSS Referer anti‑leech whitelist as well, matching the CDN’s Referer whitelist.&lt;/p>
&lt;h3 id="option-2">
&lt;a class="heading-anchor-link" href="#option-2">Option 2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="option-2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Disable OSS Referer protection (allow all). Then enforce Referer checks only at the CDN. This avoids maintaining two whitelists.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-03-07-143030.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;code>Recommended: Option 2&lt;/code>&lt;/p>
&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>OSS Referer rules support ? and * wildcards, but protocol wildcards like &lt;code>https?://&lt;/code> are not supported (poorly documented). If you want to allow both &lt;code>http://alanhe.me&lt;/code> and &lt;code>https://alanhe.me&lt;/code>, list both explicitly:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Correct configuration&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">http://alanhe.me
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">https://alanhe.me
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Both are required.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Incorrect configuration&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">https?://alanhe.me
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul></description></item><item><title>Code Continues After res.send?</title><link>https://en.1991421.cn/2017/09/25/res-send/</link><pubDate>Mon, 25 Sep 2017 09:28:20 +0800</pubDate><guid>https://en.1991421.cn/2017/09/25/res-send/</guid><description>&lt;blockquote>
&lt;p>Express is one of the most popular Node.js frameworks. Its middleware model and routing make it easy to get started, but that flexibility raises the bar for maintainability. Small oversights can create bugs that only surface later.&lt;/p>
&lt;/blockquote>
&lt;p>Here’s a simple example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/c&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">conf2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;../conf&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">conf2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">a&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;hello&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run it and you’ll see “hello” in the console even after &lt;code>res.send&lt;/code>. To stop execution, return immediately:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/c&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">conf2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;../conf&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">conf2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">a&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;hello&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now the handler exits. The Express docs omit &lt;code>return&lt;/code> because their examples end with &lt;code>res.send&lt;/code>; nothing follows. Once your logic becomes more complex, though, you must &lt;code>return&lt;/code> when short-circuiting.&lt;/p>
&lt;p>Consider this download endpoint. Without the &lt;code>return&lt;/code>, the remainder of the callback would still run after sending a 404:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/download&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">ua&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">parser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">headers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;user-agent&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">redisClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">reply&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">404&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;The download link has expired&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">obj&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">reply&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">filePath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">directory&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">filename&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;Edge&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Chrome&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Firefox&amp;#39;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">filename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Download error&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">mimetype&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-type&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mimetype&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;IE&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Disposition&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;attachment; filename=&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">encodeURIComponent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filename&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Disposition&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;attachment; filename=&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filename&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;binary&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">filestream&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createReadStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filestream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Takeaway: when the route should stop processing, &lt;code>return res.send(...)&lt;/code> (or &lt;code>res.end&lt;/code>, etc.) so the rest of the handler doesn’t continue executing.&lt;/p></description></item><item><title>Angular Development - loadChildren Duplicate Path Issue</title><link>https://en.1991421.cn/2017/09/24/angular-loadchildren/</link><pubDate>Sun, 24 Sep 2017 22:47:26 +0800</pubDate><guid>https://en.1991421.cn/2017/09/24/angular-loadchildren/</guid><description>&lt;blockquote>
&lt;p>Angular development modularizes all resources. As web systems grow larger, lazy loading is often used to improve performance. However, due to design reasons, the following design situation may occur:&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-24-144753.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-24-144849.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>The specific lazy loading code looks like this:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-24-145008.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-24-145032.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Running this configuration will result in the following error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ERROR&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">Duplicated&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">loadChildren&lt;/span> &lt;span class="n">detected&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;./security/security.module#SecurityModule&amp;#34;&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">used&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="n">loadChildren&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">but&lt;/span> &lt;span class="n">they&lt;/span> &lt;span class="n">point&lt;/span> &lt;span class="n">to&lt;/span> &lt;span class="n">different&lt;/span> &lt;span class="n">modules&lt;/span> &lt;span class="s2">&amp;#34;(/Users/heqiang/GitHub/angular-demo/src/app/parent/child/security/security.module.ts and &amp;#34;&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">Users&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">heqiang&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">GitHub&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">angular&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">demo&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">security&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">security&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">module&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ts&lt;/span>&lt;span class="s2">&amp;#34;). Webpack cannot distinguish on context and would fail to load the proper one.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="solutions">
&lt;a class="heading-anchor-link" href="#solutions">Solutions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solutions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>In the official repository issues, the community suggested using absolute paths for configuration&lt;/li>
&lt;li>Modify module names and the folder names where modules are located. If they&amp;rsquo;re different, there won&amp;rsquo;t be a duplication problem&lt;/li>
&lt;/ul>
&lt;p>Analyzing this issue, it&amp;rsquo;s actually due to limitations in the official CLI tool. Hopefully future versions can resolve this.&lt;/p></description></item><item><title>How to Use sz/rz for Uploads and Downloads on Linux (Step-by-Step Guide)</title><link>https://en.1991421.cn/2017/09/24/linuxszrz/</link><pubDate>Sun, 24 Sep 2017 11:43:49 +0800</pubDate><guid>https://en.1991421.cn/2017/09/24/linuxszrz/</guid><description>&lt;blockquote>
&lt;p>In daily development, we often need to upload files to a server or download files from it. FTP requires setting up an FTP server and configuring the service. Using sz/rz enables simple upload and download operations.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># On macOS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## On Linux machines&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">yum install lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## FreeBSD&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pkg install lrzsz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">## Debian&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">apt install -y lszrz
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="usage">
&lt;a class="heading-anchor-link" href="#usage">Usage&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usage"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="sz-usage-download">
&lt;a class="heading-anchor-link" href="#sz-usage-download">sz usage: download&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sz-usage-download"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Download a single file:
&lt;code># sz filename&lt;/code>&lt;/li>
&lt;li>Download multiple files:
&lt;code># sz filename1 filename2&lt;/code>&lt;/li>
&lt;li>Download all files under the dir directory, excluding subfolders:
&lt;code># sz dir/*&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="rz-usage-upload">
&lt;a class="heading-anchor-link" href="#rz-usage-upload">rz usage: upload&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rz-usage-upload"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>Just type the rz command
&lt;code># rz&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Force overwrite the source file
&lt;code># rz -y&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="iterm2-setup-on-macos">
&lt;a class="heading-anchor-link" href="#iterm2-setup-on-macos">iTerm2 setup on macOS&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="iterm2-setup-on-macos"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create two files in the /usr/loal/bin directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> /usr/local/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wget https://raw.githubusercontent.com/RobberPhex/iterm2-zmodem/master/iterm2-recv-zmodem.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wget https://raw.githubusercontent.com/RobberPhex/iterm2-zmodem/master/iterm2-send-zmodem.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Grant execute permissions to these two files&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod &lt;span class="m">777&lt;/span> /usr/local/bin/iterm2-*
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Tomcat Configuration Gotchas</title><link>https://en.1991421.cn/2017/09/20/tomcat/</link><pubDate>Wed, 20 Sep 2017 13:18:23 +0800</pubDate><guid>https://en.1991421.cn/2017/09/20/tomcat/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-20-052956.jpg"
alt="Tomcat illustration"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="request-garbling-encoding">
&lt;a class="heading-anchor-link" href="#request-garbling-encoding">Request Garbling (Encoding)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="request-garbling-encoding"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Add &lt;code>URIEncoding=&amp;quot;UTF-8&amp;quot;&lt;/code> in &lt;code>conf/server.xml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;Connector&lt;/span> &lt;span class="na">port=&lt;/span>&lt;span class="s">&amp;#34;8080&amp;#34;&lt;/span> &lt;span class="na">protocol=&lt;/span>&lt;span class="s">&amp;#34;HTTP/1.1&amp;#34;&lt;/span> &lt;span class="na">connectionTimeout=&lt;/span>&lt;span class="s">&amp;#34;20000&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">redirectPort=&lt;/span>&lt;span class="s">&amp;#34;8443&amp;#34;&lt;/span> &lt;span class="na">URIEncoding=&lt;/span>&lt;span class="s">&amp;#34;UTF-8&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>By default Tomcat uses &lt;code>ISO-8859-1&lt;/code> for request/response encoding. See the &lt;a href="https://wiki.apache.org/tomcat/FAQ/CharacterEncoding" target="_blank" rel="noopener">official docs&lt;/a> for more details.&lt;/p></description></item><item><title>Windows CMD: Common Commands</title><link>https://en.1991421.cn/2017/09/12/windows/</link><pubDate>Tue, 12 Sep 2017 09:35:51 +0800</pubDate><guid>https://en.1991421.cn/2017/09/12/windows/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-12-07-135536.png"
alt="command-shot"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>Windows CMD lags far behind Linux bash and macOS Terminal, but it’s still useful. Here are common commands I use.&lt;/p>
&lt;/blockquote>
&lt;h2 id="environment-variables">
&lt;a class="heading-anchor-link" href="#environment-variables">Environment Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Show env var &lt;code>NODE_ENV&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;code>set $NODE_ENV&lt;/code>&lt;/p>
&lt;ul>
&lt;li>Set env var &lt;code>NODE_ENV&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;code>set NODE_ENV=&amp;quot;production&amp;quot;&lt;/code>&lt;/p>
&lt;hr>
&lt;h2 id="networking">
&lt;a class="heading-anchor-link" href="#networking">Networking&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="networking"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>View port usage
&lt;code>netstat -ano&lt;/code>&lt;/li>
&lt;li>Find processes using a specific port
&lt;code>netstat -ano|findstr &amp;quot;3001&amp;quot;&lt;/code>&lt;/li>
&lt;li>Find process by PID
&lt;code>tasklist|findstr &amp;quot;22222&amp;quot;&lt;/code>&lt;/li>
&lt;li>Kill process
&lt;code>taskkill /f /t /im node.exe&lt;/code>&lt;/li>
&lt;/ul></description></item><item><title>A Lightweight App Development Approach</title><link>https://en.1991421.cn/2017/08/24/42d65d6/</link><pubDate>Thu, 24 Aug 2017 22:42:43 +0800</pubDate><guid>https://en.1991421.cn/2017/08/24/42d65d6/</guid><description>&lt;blockquote>
&lt;p>Building an app means coordinating both frontend and backend. With so many options, the right choice depends on the problem. After shipping several apps, here’s the lightweight stack I reach for.&lt;/p>
&lt;/blockquote>
&lt;h2 id="frontend-options">
&lt;a class="heading-anchor-link" href="#frontend-options">Frontend options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frontend-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Native&lt;/strong> — Highest development and maintenance cost because you’re essentially building two apps (iOS and Android). If you need polish and top-tier performance, native is still the gold standard.&lt;/li>
&lt;li>&lt;strong>React Native&lt;/strong> — Reduces duplication by sharing components across platforms, but custom features still require native modules, so some native knowledge remains necessary.&lt;/li>
&lt;li>&lt;strong>Hybrid&lt;/strong> — Tools like Cordova wrap a web app in a native shell and expose device APIs to JavaScript.&lt;/li>
&lt;/ul>
&lt;h2 id="backend-options">
&lt;a class="heading-anchor-link" href="#backend-options">Backend options&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="backend-options"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Plenty of choices—PHP, .NET, Java, Python, etc. Whatever you choose, design a clean RESTful API for the frontend to consume.&lt;/p>
&lt;h2 id="recommended-lightweight-stack">
&lt;a class="heading-anchor-link" href="#recommended-lightweight-stack">Recommended lightweight stack&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="recommended-lightweight-stack"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For small apps, I like &lt;strong>Ionic + Express.js&lt;/strong>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-26-154720.jpg"
alt="ionic&amp;#43;expressjs"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="why-it-works">
&lt;a class="heading-anchor-link" href="#why-it-works">Why it works&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-it-works"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Ionic is a mature hybrid framework (30k+ GitHub stars). Ionic 3 delivers a big performance boost over Ionic 1, and Ionic 4 moves even closer to native feel. The Angular + TypeScript combo is a productive environment.&lt;/li>
&lt;li>Express.js is the de facto Node.js web framework. It’s minimalist and excels at building RESTful APIs.&lt;/li>
&lt;/ul>
&lt;p>Need a web-based admin console for the backend? Drop in something like &lt;a href="https://github.com/akveo/ng2-admin" target="_blank" rel="noopener">ng2-admin&lt;/a> and keep moving.&lt;/p></description></item><item><title>Install and Configure vsftpd on Linux</title><link>https://en.1991421.cn/2017/08/19/linuxvsftpd/</link><pubDate>Sat, 19 Aug 2017 09:57:02 +0800</pubDate><guid>https://en.1991421.cn/2017/08/19/linuxvsftpd/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-26-082519.jpg"
alt="vsftpd"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>When deploying a web service, you need file transfer. For single-file transfers, sz/rz is enough. For multiple files, FTP is more convenient.
vsftpd is a classic server-side software in Linux. As it says itself: Probably the most secure and fastest FTP server for UNIX-like systems.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install software&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ yum install -y vsftpd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Enable auto-start&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ chkconfig on vsftpd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Start service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ service vsftpd start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="configuration">
&lt;a class="heading-anchor-link" href="#configuration">Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Config file location: &lt;code>/etc/vsftpd/vsftpd.conf&lt;/code>&lt;/p>
&lt;p>It is recommended to make a backup so you can restore it if it breaks: &lt;code>cp vsftpd.conf vsftpd.conf.bak&lt;/code>&lt;/p>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://desert3.iteye.com/blog/1685734" target="_blank" rel="noopener">vsftp settings on CentOS, anonymous &amp;amp; local users, PORT/PASV modes&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>HTML Anchor Links Inside Email</title><link>https://en.1991421.cn/2017/08/10/html/</link><pubDate>Thu, 10 Aug 2017 20:55:11 +0800</pubDate><guid>https://en.1991421.cn/2017/08/10/html/</guid><description>&lt;blockquote>
&lt;p>Marketing newsletters often look like web pages—these are HTML emails, a subset of HTML. Whether a feature works depends on the mail client, much like browser compatibility on the web.&lt;/p>
&lt;/blockquote>
&lt;p>A teammate recently built an email template with anchors linking to sections of the message. Android mail clients handled the anchors, but Apple’s Mail app on iOS ignored them, and QQ Mail on iOS rewrote the link into a local file-system path—clearly a compatibility issue.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-10-133346.jpg"
alt="HTML Anchor"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-10-135235.jpg"
alt="QQ mail"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="client-support">
&lt;a class="heading-anchor-link" href="#client-support">Client support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="client-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">Email client&lt;/th>
&lt;th style="text-align: left">Anchor support&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">Gmail (Web)&lt;/td>
&lt;td style="text-align: left">YES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Gmail (Android)&lt;/td>
&lt;td style="text-align: left">YES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Inbox by Gmail (Android)&lt;/td>
&lt;td style="text-align: left">YES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Gmail (iOS)&lt;/td>
&lt;td style="text-align: left">NO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Apple Mail (iOS)&lt;/td>
&lt;td style="text-align: left">NO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Apple Mail (macOS)&lt;/td>
&lt;td style="text-align: left">YES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Yahoo! Mail (Web)&lt;/td>
&lt;td style="text-align: left">YES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Outlook.com (Web)&lt;/td>
&lt;td style="text-align: left">NO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Outlook (Android)&lt;/td>
&lt;td style="text-align: left">NO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Outlook (Windows desktop)&lt;/td>
&lt;td style="text-align: left">YES&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">Outlook (macOS)&lt;/td>
&lt;td style="text-align: left">NO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">QQ Mail (iOS)&lt;/td>
&lt;td style="text-align: left">NO&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://www.ruanyifeng.com/blog/2013/06/html_email.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2013/06/html_email.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://emaildesign.beefree.io/2016/09/how-to-add-anchor-links-in-email/" target="_blank" rel="noopener">https://emaildesign.beefree.io/2016/09/how-to-add-anchor-links-in-email/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>DDNS Explained (Simple Guide)</title><link>https://en.1991421.cn/2017/08/04/ddns/</link><pubDate>Fri, 04 Aug 2017 19:10:23 +0800</pubDate><guid>https://en.1991421.cn/2017/08/04/ddns/</guid><description>&lt;p>Recently, I got a &lt;code>VPS&lt;/code> that I plan to use to build a web service. However, the problem I face is that the VPS doesn&amp;rsquo;t have a fixed IP, so I can&amp;rsquo;t directly add a domain-to-IP resolution record on GoDaddy. While confused, I learned that the network environment of the server where the VPS is located has &lt;a href="https://en.wikipedia.org/wiki/Dynamic_DNS" target="_blank" rel="noopener">Dynamic DNS&lt;/a>. I didn&amp;rsquo;t understand this technology before, so I spent some time on Wikipedia and finally figured it out.&lt;/p>
&lt;p>&lt;code>DDNS&lt;/code> is designed to solve the dynamic IP problem. You can use DDNS to give a fixed dynamic domain name, such as [aaaaaaa.asuscomm.com], which is bound to the web service, so you can directly use this dynamic domain name to access it.&lt;/p>
&lt;p>Of course, when we normally build a website, we certainly don&amp;rsquo;t want to use this domain name directly. What should we do? It&amp;rsquo;s easy—just add a record to your own domain name, such as alanhe.me.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-04-112143.jpg"
alt="CNAME"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After successful configuration as above, you can use &lt;a href="http://test.alanhe.me" target="_blank" rel="noopener">http://test.alanhe.me&lt;/a> to access the web you built.&lt;/p>
&lt;h2 id="summary">
&lt;a class="heading-anchor-link" href="#summary">Summary&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="summary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;code>DDNS&lt;/code> itself is to solve the problem of dynamic IP needing a fixed domain name.&lt;/li>
&lt;li>&lt;code>DNS&lt;/code> record types:
&lt;ul>
&lt;li>A record (IP pointing)
The target host address type must be an IP address.&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/CNAME_record" target="_blank" rel="noopener">CNAME (alias pointing)&lt;/a>
Can set aliases for hosts, equivalent to using subdomains instead of IP addresses. If the IP address changes, only the subdomain resolution needs to be modified.&lt;/li>
&lt;li>MX
Mail exchange record.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Related article: &lt;a href="https://www.douban.com/note/523329196/" target="_blank" rel="noopener">What are A records, CNAME, MX records, and NS records in domain name resolution&lt;/a>&lt;/p></description></item><item><title>[Translation] webpack 3: Official Release</title><link>https://en.1991421.cn/2017/07/25/webpack3/</link><pubDate>Tue, 25 Jul 2017 12:18:09 +0800</pubDate><guid>https://en.1991421.cn/2017/07/25/webpack3/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-07-29-1-Ac4K68j43uSbvHnKZKfXPw.jpeg"
alt="It’s finally here. And it’s beautiful."
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Original: &lt;a href="https://medium.com/webpack/webpack-3-official-release-15fd2dd8f07b" target="_blank" rel="noopener">webpack-3-official-release&lt;/a>&lt;/p>
&lt;p>Since v2, we promised to ship community‑voted features on a faster, more stable cadence. After a year of betas without breaking changes between releases, we’re announcing &lt;code>webpack 3.0.0&lt;/code> — ready to download and upgrade.&lt;/p>
&lt;p>&lt;code>npm install webpack@3.0.0 --save-dev&lt;/code>&lt;/p>
&lt;p>or&lt;/p>
&lt;p>&lt;code>yarn add webpack@3.0.0 --dev&lt;/code>&lt;/p>
&lt;p>Upgrading from v2 should require little more than the install, though internal changes may affect some plugins. So far, 98% of users report no breaking changes.&lt;/p>
&lt;h2 id="highlights">
&lt;a class="heading-anchor-link" href="#highlights">Highlights&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="highlights"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As promised, we’re shipping features you voted for — thanks to contributors and sponsors for making it possible.&lt;/p>
&lt;h3 id="scope-hoisting">
&lt;a class="heading-anchor-link" href="#scope-hoisting">Scope Hoisting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="scope-hoisting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Webpack traditionally wrapped each module in a function, which adds overhead. Tools like Closure Compiler and Rollup “hoist” modules into a single scope for faster execution. Webpack 3 adds similar optimization.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-07-29-124018.jpg"
alt="Bundle size comparison"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Enable it via:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">plugins&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nx">webpack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">optimize&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ModuleConcatenationPlugin&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Scope hoisting primarily targets ECMAScript modules; webpack will bail out in certain cases. Use &lt;code>--display-optimization-bailout&lt;/code> to learn why a bailout occurred.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-07-30-074058.jpg"
alt="fallback"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>You may see a small bundle size reduction, but the bigger win is runtime performance in the browser.&lt;/p>
&lt;h3 id="magic-comments">
&lt;a class="heading-anchor-link" href="#magic-comments">Magic Comments&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="magic-comments"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>With dynamic &lt;code>import()&lt;/code> in v2, users couldn’t name chunks like with &lt;code>require.ensure&lt;/code>. Magic comments fix that — you can name chunks inline:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">import(/* webpackChunkName: &amp;#34;my-chunk-name&amp;#34; */ &amp;#39;module&amp;#39;);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This shipped in 2.4/2.6 and is now stable. Dynamic imports now match &lt;code>require.ensure&lt;/code> in flexibility. See the docs on code splitting: &lt;a href="https://webpack.js.org/guides/code-splitting-async" target="_blank" rel="noopener">link&lt;/a>&lt;/p>
&lt;h2 id="whats-next">
&lt;a class="heading-anchor-link" href="#whats-next">What’s next&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="whats-next"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Planned improvements:&lt;/p>
&lt;ul>
&lt;li>Better build caching&lt;/li>
&lt;li>Faster startup and incremental builds&lt;/li>
&lt;li>Improved TypeScript experience&lt;/li>
&lt;li>Long‑term caching&lt;/li>
&lt;li>WASM module support&lt;/li>
&lt;li>UX improvements&lt;/li>
&lt;/ul>
&lt;h2 id="thanks">
&lt;a class="heading-anchor-link" href="#thanks">Thanks&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="thanks"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To all users, contributors, writers, bloggers, sponsors, and maintainers — thank you. If you’d like to support the project, consider donating via Open Collective.&lt;/p></description></item><item><title>Solving Lazy Loading Failures in Angular AOT Builds</title><link>https://en.1991421.cn/2017/07/22/aot-lazy-load-fail/</link><pubDate>Sat, 22 Jul 2017 18:10:24 +0800</pubDate><guid>https://en.1991421.cn/2017/07/22/aot-lazy-load-fail/</guid><description>&lt;h2 id="the-problem">
&lt;a class="heading-anchor-link" href="#the-problem">The Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>As Angular applications grow in complexity, the number of modules and components increases, leading to a larger bundle size. To prevent a sluggish initial load and improve user experience, Angular provides &lt;strong>Lazy Loading&lt;/strong>.&lt;/p>
&lt;p>Ideally, lazy loading splits the application into multiple JavaScript chunks, allowing the browser to load specific modules only when the user navigates to the corresponding route.&lt;/p>
&lt;p>However, during an AOT (Ahead-of-Time) build, I noticed that these separate lazy-loaded chunks were missing. Instead, &lt;code>main.js&lt;/code> was unexpectedly large. A quick search within the bundle revealed that logic which should have been lazy-loaded was actually embedded in the main file. Interestingly, the lazy loading worked perfectly fine when using a JIT (Just-in-Time) build.&lt;/p>
&lt;p>This discrepancy suggested a bug or incompatibility in the build toolchain.
The version causing the issue was:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;@ngtools/webpack&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;1.2.3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-solution">
&lt;a class="heading-anchor-link" href="#the-solution">The Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The fix was as simple as upgrading the build tool. After updating to version &lt;strong>1.3.1&lt;/strong>, the AOT build correctly generated the separate lazy-loaded chunks.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install @ngtools/webpack@1.3.1 --save-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Re-running the AOT build confirmed that the splitting was working as intended:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/ng-bundle.jpg"
alt="Angular Bundle Output"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="important-notes">
&lt;a class="heading-anchor-link" href="#important-notes">Important Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="important-notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Memory Usage&lt;/strong>: AOT compilation is resource-intensive. If your build fails with &amp;ldquo;JavaScript heap out of memory&amp;rdquo; errors, you may need to increase the Node.js memory limit. You can find more details on how to do that &lt;a href="https://en.1991421.cn/2017/07/22/7fa2b445/" target="_blank" rel="noopener">here&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Version Compatibility&lt;/strong>: You don&amp;rsquo;t always need the absolute latest version, as build tools have complex dependencies. Match your tools to your Angular version. For instance, my project used Angular &lt;code>4.0.1&lt;/code> (released March 2017), so I chose &lt;code>@ngtools/webpack&lt;/code> &lt;code>1.3.1&lt;/code> (released April 2017) to ensure compatibility.&lt;/li>
&lt;li>&lt;strong>Server-Side Optimization&lt;/strong>: For production deployments, always enable &lt;strong>GZIP&lt;/strong> or &lt;strong>Brotli&lt;/strong> compression. This typically reduces the payload size by over 50%, significantly speeding up the initial load time.&lt;/li>
&lt;/ul></description></item><item><title>JavaScript heap out of memory</title><link>https://en.1991421.cn/2017/07/22/javascript-heap-out-of-memory/</link><pubDate>Sat, 22 Jul 2017 17:07:56 +0800</pubDate><guid>https://en.1991421.cn/2017/07/22/javascript-heap-out-of-memory/</guid><description>&lt;h2 id="problem">
&lt;a class="heading-anchor-link" href="#problem">Problem&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="problem"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Recently during Angular development involving build packaging, JIT packaging worked fine, but when doing AOT packaging, it reported &lt;code>JavaScript heap out of memory&lt;/code> and memory overflow, as shown below:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/JavaScript%20heap%20out%20of%20memory.jpg"
alt="out of memory"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>After investigation, this issue can be resolved by setting higher memory space.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Increase &lt;code>max_old_space_size&lt;/code>&lt;/p>
&lt;p>For example, in my case where webpack packaging script commands are defined in package.json, it looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;webpack&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="s2">&amp;#34;node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After making this change, the packaging works fine.&lt;/p>
&lt;h2 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Memory unit is &lt;code>MB&lt;/code>&lt;/li>
&lt;li>AOT compared to JIT has an additional ngc compilation step, so it does consume more memory.&lt;/li>
&lt;/ul></description></item><item><title>Nginx Redirect POST Request Data Loss Issue</title><link>https://en.1991421.cn/2017/07/19/nginx-post/</link><pubDate>Wed, 19 Jul 2017 06:55:18 +0800</pubDate><guid>https://en.1991421.cn/2017/07/19/nginx-post/</guid><description>&lt;blockquote>
&lt;/blockquote>
&lt;p>Created two websites, Website A and Website B. Website A receives POST submissions and then redirects to Website B. The redirection is configured in Nginx. For specific configuration syntax, please refer to the official Nginx documentation. Here I&amp;rsquo;ll only show the key statements.&lt;/p>
&lt;h2 id="original-configuration">
&lt;a class="heading-anchor-link" href="#original-configuration">Original Configuration&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="original-configuration"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># rewrite ^.+ http://b.com$uri；
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>However, after testing, we discovered an issue: when redirecting to B, the form submission data would be lost.&lt;/p>
&lt;h2 id="solution">
&lt;a class="heading-anchor-link" href="#solution">Solution&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After researching, we found that &lt;code>rewrite&lt;/code> indeed causes POST data loss. Therefore, we should use &lt;code>307&lt;/code> redirection. The specific configuration is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">return 307 $scheme://b.com$request_uri;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="explanation">
&lt;a class="heading-anchor-link" href="#explanation">Explanation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="explanation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="http-status-code-307-meaning">
&lt;a class="heading-anchor-link" href="#http-status-code-307-meaning">HTTP Status Code 307 Meaning&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="http-status-code-307-meaning"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307" target="_blank" rel="noopener">HTTP-307&lt;/a> is a temporary redirect, indicating that the request has been temporarily moved to the given target address. Both the method and request body will be reused for the redirected request.&lt;/p></description></item><item><title>Pitfalls When Wrapping an H5 Site with Cordova</title><link>https://en.1991421.cn/2017/07/14/cordovah5/</link><pubDate>Fri, 14 Jul 2017 21:55:27 +0800</pubDate><guid>https://en.1991421.cn/2017/07/14/cordovah5/</guid><description>&lt;blockquote>
&lt;p>倘若存在移动网页，然后需要APP，但是只是作为一种存在介质的话，cordova来将其封装打包，最为合适，但是当真正去做还是会有坑。
将其封装为APP，主要使用到了这个插件&lt;a href="https://github.com/apache/cordova-plugin-inappbrowser" target="_blank" rel="noopener">cordova-plugin-inappbrowser&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h2 id="pitfall-1--file-downloads">
&lt;a class="heading-anchor-link" href="#pitfall-1--file-downloads">Pitfall 1 — file downloads&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pitfall-1--file-downloads"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>在实际APP中，点击下载，发现没反应，几次都不行，推断有问题，在GitHub插件pr中检索download,果然，发现有相关问题，如下图，仔细看了下，得到以下两点信息
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/inappbrowser-pr.png"
alt="inappbrowser-pr"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ul>
&lt;li>首先这个下载问题，的确是这个插件的事，本质原因是安卓手机下载，就意味着文件要存储在手机上，而这是需要授权的，所以插件必须要声明文件存储权限，然后看了下插件的源码，果然，根本没有存储权限&lt;/li>
&lt;li>是pr，说明已经解决，并且提交，申请合并主干了，但是由于pr是打开状态，说明还没合并主干，那么通过npm，cordova安装的都不行了，因为官方版本不行，所以只能根据提交记录找到提交记录作者的fork状态下的插件即可，然后下载下来丢到项目中对应插件下，重新添加平台，构建打包，发现，果然解决啦。
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/inappbrowser-pull.png"
alt="inappbrowser-pull"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/inappbrowser-fork.png"
alt="inappbrowser-fork"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/li>
&lt;/ul>
&lt;p>解决问题的插件分支，&lt;a href="https://github.com/MeirBon/cordova-plugin-inappbrowser/tree/download-permissions" target="_blank" rel="noopener">点击下载&lt;/a>&lt;/p>
&lt;h2 id="pitfall-2--splash-screen">
&lt;a class="heading-anchor-link" href="#pitfall-2--splash-screen">Pitfall 2 — splash screen&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pitfall-2--splash-screen"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>按照启动动画默认的设置，实际运行，发现启动动画消失后会有长时间的白屏问题，分析发现，是inappbrowser在打开网页的时候是会有一段时间的白屏，那么这个就造成很不好的体验，需要实现启动动画一直显示，然后如果事件监听到网页加载完成了，就手动关闭启动动画，这样子是最好，查看插件官方文档，有这么个参数配置
&lt;code>&amp;lt;preference name=&amp;quot;AutoHideSplashScreen&amp;quot; value=&amp;quot;false&amp;quot; /&amp;gt;&lt;/code>
按照配置说明是设置为false,就不会自动关闭，需要手动关闭
但是实际配置后，添加平台，运行，发现不起作用，查看pr,果然有人提PR，
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/cordova-plugin-splashscreen-pull.png"
alt="cordova-plugin-splashscreen-pull"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
又是open状态，没招。&lt;/p>
&lt;p>最终只是绕路解决，想到的方案是配置文件中将splashscreen的时效设置为0，然后手动启动和关闭，但是因为启动会自动消失，所以写定时器循环，不断的show，保证splash不消失，具体代码如下&lt;/p>
&lt;ul>
&lt;li>&lt;code>config.xml&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;preference&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;ShowSplashScreenSpinner&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;preference&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;SplashScreenDelay&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;0&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;preference&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;FadeSplashScreen&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;preference&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;FadeSplashScreenDuration&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;0&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>index.js&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">onDeviceReady&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">receivedEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;deviceready&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;http://apache.org&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="n">target&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;_blank&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="n">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;location=no,toolbar=no&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">var&lt;/span> &lt;span class="n">intervalID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">window&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">setInterval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">console&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;navigator.splashscreen.show()&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">navigator&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">splashscreen&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">show&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span> &lt;span class="mi">500&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inAppBrowserRef&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cordova&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">InAppBrowser&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inAppBrowserRef&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;loadstart&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">loadStartCallBack&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inAppBrowserRef&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;loadstop&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">loadStopCallBack&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">function&lt;/span> &lt;span class="n">loadStartCallBack&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">inAppBrowserRef&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hide&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">function&lt;/span> &lt;span class="n">loadStopCallBack&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="n">inAppBrowserRef&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">show&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">navigator&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">splashscreen&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">clearInterval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">intervalID&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">//&lt;/span> &lt;span class="o">//&lt;/span> &lt;span class="n">We&lt;/span>&lt;span class="s1">&amp;#39;re done initializing, remove the splash screen&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">navigator&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">splashscreen&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hide&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">console&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">warn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Hiding splash screen&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>测试效果还不错&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Apache基金会缺钱，缺人，这些插件都是常年累月没人更新，这样造成很多问题无法及时解决，久而久之的话，这些开源技术也就不会有人使用了&lt;/li>
&lt;li>Google、Github、Stackoverflow，很多时候问题都是在这上面寻找，都是可以找到相关问题的描述，大众点的问题，一般都会有人提出了解决方案&lt;/li>
&lt;li>Cordova本身这个技术，热度看来在减少，毕竟Reactive Native很成熟，NativeScript也在崛起，类似需求，其实用别的也能实现。&lt;/li>
&lt;/ul></description></item><item><title>Redis Installation Guide on Linux</title><link>https://en.1991421.cn/2017/07/11/85e41428/</link><pubDate>Tue, 11 Jul 2017 23:17:34 +0800</pubDate><guid>https://en.1991421.cn/2017/07/11/85e41428/</guid><description>&lt;blockquote>
&lt;p>Redis officially provides source installation and an official tutorial. If you prefer the original version, &lt;a href="https://redis.io/topics/quickstart" target="_blank" rel="noopener">click here&lt;/a>. This post is more of a translation and some extra notes.
Also a quick review of common commands.&lt;/p>
&lt;/blockquote>
&lt;h2 id="installation">
&lt;a class="heading-anchor-link" href="#installation">Installation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official approach is source installation.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ wget http://download.redis.io/redis-stable.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 解压，释放源代码文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ tar xvzf redis-stable.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ &lt;span class="nb">cd&lt;/span> redis-stable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 编译&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ make
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the &lt;code>src&lt;/code> directory, you will see these executable scripts:&lt;/p>
&lt;ul>
&lt;li>&lt;em>redis-server&lt;/em> the Redis server itself&lt;/li>
&lt;li>&lt;em>redis-sentinel&lt;/em> is the Redis Sentinel executable (monitoring and failover).&lt;/li>
&lt;li>&lt;em>redis-cli&lt;/em> Redis interactive CLI&lt;/li>
&lt;li>&lt;em>redis-benchmark&lt;/em> used to check Redis performance&lt;/li>
&lt;li>&lt;em>redis-check-aof&lt;/em> and &lt;em>redis-check-dump&lt;/em> are useful in the rare event of corrupted data files.&lt;/li>
&lt;/ul>
&lt;h3 id="copy-the-binaries">
&lt;a class="heading-anchor-link" href="#copy-the-binaries">Copy the binaries&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="copy-the-binaries"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Run the following two commands in &lt;code>src&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo cp src/redis-server /usr/local/bin/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo cp src/redis-cli /usr/local/bin/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Or simply run &lt;code>sudo make install&lt;/code>.&lt;/p>
&lt;h2 id="startup">
&lt;a class="heading-anchor-link" href="#startup">Startup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="startup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>In practice, you should set it up as a service with auto-start for easier management. For example:&lt;/p>
&lt;ul>
&lt;li>Create directories for Redis config and data&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sudo&lt;/span> &lt;span class="n">mkdir&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">etc&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">redis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sudo&lt;/span> &lt;span class="n">mkdir&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">redis&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Copy the init script under &lt;code>utils&lt;/code> to &lt;code>/etc/init.d&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo cp utils/redis_init_script /etc/init.d/redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Edit the init script&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">sudo vi /etc/init.d/redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Specific edits&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1">#chkconfig: 2345 80 90&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Simple Redis init.d script conceived to work on Linux systems&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># as it does use of the /proc filesystem.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">REDISPORT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">6379&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">EXEC&lt;/span>&lt;span class="o">=&lt;/span>/usr/local/redis/bin/redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CLIEXEC&lt;/span>&lt;span class="o">=&lt;/span>/usr/local/redis/bin/redis-cli
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PIDFILE&lt;/span>&lt;span class="o">=&lt;/span>/var/run/redis_&lt;span class="si">${&lt;/span>&lt;span class="nv">REDISPORT&lt;/span>&lt;span class="si">}&lt;/span>.pid
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CONF&lt;/span>&lt;span class="o">=&lt;/span>”/etc/redis/&lt;span class="si">${&lt;/span>&lt;span class="nv">REDISPORT&lt;/span>&lt;span class="si">}&lt;/span>.conf”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">case&lt;/span> “&lt;span class="nv">$1&lt;/span>” in
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">start&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -f &lt;span class="nv">$PIDFILE&lt;/span> &lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “&lt;span class="nv">$PIDFILE&lt;/span> exists, process is already running or crashed”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “Starting Redis server…”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$EXEC&lt;/span> &lt;span class="nv">$CONF&lt;/span> &lt;span class="p">&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">stop&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> ! -f &lt;span class="nv">$PIDFILE&lt;/span> &lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “&lt;span class="nv">$PIDFILE&lt;/span> does not exist, process is not running”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>cat &lt;span class="nv">$PIDFILE&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “Stopping …”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$CLIEXEC&lt;/span> -p &lt;span class="nv">$REDISPORT&lt;/span> shutdown
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="o">[&lt;/span> -x /proc/&lt;span class="si">${&lt;/span>&lt;span class="nv">PID&lt;/span>&lt;span class="si">}&lt;/span> &lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “Waiting &lt;span class="k">for&lt;/span> Redis to shutdown …”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “Redis stopped”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> “Please use start or stop as first argument”
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">esac&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compared with the original config, the key differences are:&lt;/p>
&lt;ol>
&lt;li>&lt;code>#chkconfig: 2345 80 90&lt;/code>&lt;/li>
&lt;li>&lt;code>$EXEC $CONF &amp;amp;&lt;/code>&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Register the service&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># 注册服务
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ chkconfig -add redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Enable auto-start&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ chkconfig redis on
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="install-via-yum">
&lt;a class="heading-anchor-link" href="#install-via-yum">Install via yum&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="install-via-yum"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The above is source installation, which is more tedious. Using yum is much simpler.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ yum install -y redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After installation, just start the service.&lt;/p></description></item><item><title>How to Use Git Submodule (Step-by-Step Guide)</title><link>https://en.1991421.cn/2017/07/09/git-submodule/</link><pubDate>Sun, 09 Jul 2017 22:48:39 +0800</pubDate><guid>https://en.1991421.cn/2017/07/09/git-submodule/</guid><description>&lt;blockquote>
&lt;p>Git Submodule allows you to use a Git repository as a subdirectory of another Git repository. It lets you clone another repository into your project while keeping commits separate.&lt;/p>
&lt;/blockquote>
&lt;p>Recently, I&amp;rsquo;ve been using this in actual project development and encountered some detailed issues. So I&amp;rsquo;ve summarized common Git submodule operations below for future reference.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-17-143442.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="adding-submodules">
&lt;a class="heading-anchor-link" href="#adding-submodules">Adding Submodules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="adding-submodules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add submodule&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule add https://github.com/alanhe421/Angular-group.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Set mapping path alias lib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule add https://github.com/alanhe421/Angular-group.git lib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For specific usage settings, execute the help command&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git submodule --help
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2019-09-17-142617.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="cloning-projects-with-submodules">
&lt;a class="heading-anchor-link" href="#cloning-projects-with-submodules">Cloning Projects with Submodules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cloning-projects-with-submodules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="getting-project-code">
&lt;a class="heading-anchor-link" href="#getting-project-code">Getting Project Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-project-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>Method 1
&lt;ol>
&lt;li>Clone the project, submodule directories are cloned by default but are empty
&lt;code>git clone https://github.com/chaconinc/MainProject&lt;/code>&lt;/li>
&lt;li>Initialize submodules: initialize local configuration file
&lt;code> git submodule init&lt;/code>&lt;/li>
&lt;li>Fetch all data from that project and check out the appropriate commit listed in the superproject
&lt;code>git submodule update&lt;/code>
One command: &lt;code>git pull --recurse-submodules &amp;amp;&amp;amp; git submodule update --init --recursive&lt;/code>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>Method 2
&lt;code>git clone --recursive https://github.com/chaconinc/MainProject&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="updating-submodule-code">
&lt;a class="heading-anchor-link" href="#updating-submodule-code">Updating Submodule Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="updating-submodule-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>
&lt;p>Method 1&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> DbConnector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge origin/master
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Method 2&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git submodule update --remote DbConnector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This updates master branch by default, for other branches&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config -f .gitmodules submodule.DbConnector.branch stable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule update --remote
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge origin/master
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="committing-submodule-updates">
&lt;a class="heading-anchor-link" href="#committing-submodule-updates">Committing Submodule Updates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="committing-submodule-updates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> DbConnector
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git checkout stable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule update --remote --merge
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="removing-submodules">
&lt;a class="heading-anchor-link" href="#removing-submodules">Removing Submodules&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="removing-submodules"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Simply deleting the &lt;code>.gitmodules&lt;/code> file won&amp;rsquo;t work, because .git still contains related mapping records. So you need to execute the following commands for safe removal.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">rm -rf .git/modules
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove module info recorded in .gitmodules (--cached option clears cache in .git/modules)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rm --cached &lt;span class="o">{&lt;/span>MOD_NAME&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="pulling-updated-submodule-code">
&lt;a class="heading-anchor-link" href="#pulling-updated-submodule-code">Pulling Updated Submodule Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pulling-updated-submodule-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">git submodule update --remote --merge
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="reference-documentation">
&lt;a class="heading-anchor-link" href="#reference-documentation">Reference Documentation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-documentation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules" target="_blank" rel="noopener">Git Tools - Submodules&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Ionic OAuth Authorization</title><link>https://en.1991421.cn/2017/07/09/ionicoauth/</link><pubDate>Sun, 09 Jul 2017 00:19:04 +0800</pubDate><guid>https://en.1991421.cn/2017/07/09/ionicoauth/</guid><description>&lt;h2 id="background">
&lt;a class="heading-anchor-link" href="#background">Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>I recently developed an app, CodeTracker, which uses the WakaTime API to display data. There are two ways to access the data: an API key from the user&amp;rsquo;s WakaTime account, or OAuth2 authorization. With OAuth, users log in and authorize the app, which then receives a token.&lt;/p>
&lt;p>Entering an API key is simplest, but API keys are long and impractical for users to remember. So I implemented OAuth login. The result is below:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/wakatime-oauth.gif"
alt="wakatime-oauth"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="implementation">
&lt;a class="heading-anchor-link" href="#implementation">Implementation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="implementation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="main-code">
&lt;a class="heading-anchor-link" href="#main-code">Main code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="main-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Only the key part is shown.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@Component&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">selector&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;page-welcome&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">templateUrl&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;welcome.html&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">WelcomePage&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">string&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">loading&lt;/span>: &lt;span class="kt">Loading&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">accessToken&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">public&lt;/span> &lt;span class="nx">navCtrl&lt;/span>: &lt;span class="kt">NavController&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">private&lt;/span> &lt;span class="nx">apiService&lt;/span>: &lt;span class="kt">ApiService&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">private&lt;/span> &lt;span class="nx">authService&lt;/span>: &lt;span class="kt">AuthService&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="nx">loadingCtrl&lt;/span>: &lt;span class="kt">LoadingController&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">private&lt;/span> &lt;span class="nx">iab&lt;/span>: &lt;span class="kt">InAppBrowser&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">loading&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">loadingCtrl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">spinner&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;bubbles&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">showBackdrop&lt;/span>: &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// content: &amp;#39;Please wait...&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * WakaTime authorization
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">grantClicked() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`https://wakatime.com/oauth/authorize?client_id=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">CLIENT_ID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">&amp;amp;response_type=token&amp;amp;redirect_uri=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">REDIRECT_URI&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">&amp;amp;scope=email,read_stats`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">browser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">iab&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;_blank&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;location=no,clearsessioncache=yes,clearcache=yes&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">listener&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;loadstart&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">subscribe&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">event&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ignore the wakatime authorize screen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;oauth/authorize&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Check the redirect uri
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">REDIRECT_URI&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">listener&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">unsubscribe&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">close&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;=&amp;#39;&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;amp;&amp;#39;&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">accessToken&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">token&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">LocalSettingService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAuthorization&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">accessToken&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">login&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Could not authenticate&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="notes">
&lt;a class="heading-anchor-link" href="#notes">Notes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="notes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>In principle, the app opens an in-app browser, the user logs in and authorizes, the callback includes a token. You listen to URL changes, extract the token, then continue.&lt;/li>
&lt;li>For apps, &lt;code>REDIRECT_URI&lt;/code> is &lt;code>http://localhost&lt;/code>.&lt;/li>
&lt;li>My successful callback URI looked like:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">http://localhost/#access_token=sec_VjDDAPwUz8b6p6RezqckosgD2oNVqS0aYyuFGo4gpEYsRpIVcgmxzXTId8iKpelEszZpuzQJc4cWM6Na&amp;amp;refresh_token=ref_EDkGC1NyhcnEU76mYlg7FRuWdymLDNDUcvF4NhqT4UNUUECPqIGrM1xU2mXzSyfmTCvqn9sVdn2baO6k&amp;amp;scope=email%2Cread_stats&amp;amp;uid=1fcb745b-b90e-4660-9c2d-2ae97a8ba010&amp;amp;token_type=bearer&amp;amp;expires_in=43200
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Other OAuth systems are similar.&lt;/p>
&lt;ul>
&lt;li>If you are unclear about OAuth, I recommend Ruan Yifeng&amp;rsquo;s article &lt;a href="http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html" target="_blank" rel="noopener">Understanding OAuth 2.0&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Git Submodule Use Cases</title><link>https://en.1991421.cn/2017/07/05/7465943/</link><pubDate>Wed, 05 Jul 2017 23:50:24 +0800</pubDate><guid>https://en.1991421.cn/2017/07/05/7465943/</guid><description>&lt;h2 id="use-cases">
&lt;a class="heading-anchor-link" href="#use-cases">Use Cases&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="use-cases"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>After a year of hard work, my project had been online for some time, and I thought I could finally catch my breath. Then my manager issued a new command: we needed to develop Project B. Project B had a distinctive feature - it was very similar to Project A in terms of functionality and presentation. The manager wanted it developed quickly, so I needed to consider how to solve the architecture transformation and maximize code reuse.&lt;/p>
&lt;p>The overall technical architecture of the project I was involved in was:
&lt;code>Angular(frontend)-----------------node-express(backend)&lt;/code>&lt;/p>
&lt;p>I consulted many people and thought about it myself. Since our company projects were managed on GitLab, I came up with the following three solutions:&lt;/p>
&lt;ol>
&lt;li>Everything under one repository, with projects separated only at the folder level, controlled by parameters&lt;/li>
&lt;li>Branch-based approach, like branch-project-a, branch-project-b&lt;/li>
&lt;li>git-submodule&lt;/li>
&lt;/ol>
&lt;h2 id="solution-comparison">
&lt;a class="heading-anchor-link" href="#solution-comparison">Solution Comparison&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="solution-comparison"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Comparing these three solutions:&lt;/p>
&lt;p>Solution 1 should definitely be rejected because maintainability would be terrible. It might be convenient for short-term development, but the possibility of human errors and poor maintainability are significant drawbacks.&lt;/p>
&lt;p>Solution 2 could work, but it means multiple projects would be developed and managed under one repository. In other words, developers would have easy access to modify multiple projects, which I think is quite dangerous. Additionally, the differential development between Project A and Project B would lead to numerous branches in the future, making maintenance and merging unclear and complicated. After all, they&amp;rsquo;re not the same project - they&amp;rsquo;re just similar. So I think Solution 2 should also be eliminated.&lt;/p>
&lt;p>Solution 3, the git submodule approach, involves extracting parts of the code into an independent repository, then using git&amp;rsquo;s submodule mechanism for mapping and inclusion. The submodule itself is also a repository that can be independently developed and maintained. After introducing it into the project, you just use it like a third-party package.&lt;/p>
&lt;p>After much consideration, I believe the final solution should reflect two points: &lt;code>Project A and Project B are two separate entities, which means they must exist independently; Project A and Project B have commonalities, which determines there should be some reusability&lt;/code>. Based on this, I decided to choose Solution 3.&lt;/p>
&lt;h2 id="experience-using-it">
&lt;a class="heading-anchor-link" href="#experience-using-it">Experience Using It&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="experience-using-it"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using &lt;code>git submodules&lt;/code> is similar to Node.js development, where we use &lt;code>npm&lt;/code> to manage all third-party packages - you just use them without trying to modify them directly, because you&amp;rsquo;re just the consumer.&lt;/p>
&lt;p>The introduction and use of git submodules also forces you to extract and abstract the code in your project to a certain degree, making it adaptable to more situations. This significantly helps with code quality, flexibility, and even developers&amp;rsquo; own skill development and understanding.&lt;/p>
&lt;p>So I recommend that those who encounter similar scenarios in actual development should consider using this mechanism.&lt;/p></description></item><item><title>Automate Blog Deployment with Travis CI</title><link>https://en.1991421.cn/2017/07/04/travis-ciblog/</link><pubDate>Tue, 04 Jul 2017 22:33:54 +0800</pubDate><guid>https://en.1991421.cn/2017/07/04/travis-ciblog/</guid><description>&lt;p>Travis CI is a hosted, distributed continuous integration service for GitHub projects.&lt;/p>
&lt;h2 id="background">
&lt;a class="heading-anchor-link" href="#background">Background&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="background"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>My blog lives on GitHub. The &lt;code>master&lt;/code> branch holds the generated static site, while &lt;code>source&lt;/code> stores the Hexo project. Before automation, publishing a new post meant:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># 1. Stage changes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git add .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># 2. Commit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &amp;#39;update&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># 3. Push to remote
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># 4. Generate + deploy static site
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ hexo g -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="after-ci">
&lt;a class="heading-anchor-link" href="#after-ci">After CI&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="after-ci"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Now any push to the &lt;code>source&lt;/code> branch triggers Travis CI to build and deploy to the static-site branch automatically. I only write posts and push—the deployment step (4) is handled by CI.&lt;/p></description></item><item><title>Packaging Mobile Sites Using Cordova</title><link>https://en.1991421.cn/2017/07/02/cordova/</link><pubDate>Sun, 02 Jul 2017 22:46:11 +0800</pubDate><guid>https://en.1991421.cn/2017/07/02/cordova/</guid><description>&lt;blockquote>
&lt;p>Recently, a client made a request. Although we already have a mobile web version of the site, it&amp;rsquo;s burdensome for clients to remember and input the URL. Therefore, an APP is needed. However, the cost of developing a standalone APP is considerable. How can we maximize the reuse of existing resources, such as the mobile web version? This situation led to a solution: using hybrid development tools like Cordova to package the mobile site as an APP. This way, the APP exists, and for specific clients, the only difference between the APP and the website is the form of existence, without the need for differentiated functionality. Packaging the mobile site into an APP is the most suitable approach in this context.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/cordova-bot.png"
alt="https://static.1991421.cn/cordova-bot.png"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="feasibility">
&lt;a class="heading-anchor-link" href="#feasibility">Feasibility&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="feasibility"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="can-it-be-packaged">
&lt;a class="heading-anchor-link" href="#can-it-be-packaged">Can it be packaged?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="can-it-be-packaged"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Answer: It&amp;rsquo;s just using the system browser to open a webpage. Cordova has a browser plugin, so it&amp;rsquo;s possible.&lt;/p>
&lt;h3 id="can-it-be-published-on-app-stores">
&lt;a class="heading-anchor-link" href="#can-it-be-published-on-app-stores">Can it be published on app stores?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="can-it-be-published-on-app-stores"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Answer: We can directly state the conclusion now. Most stores currently allow it. If none did, there would be no point in reading this article.
I&amp;rsquo;ve successfully published apps packaged using this method in major stores, such as the Apple App Store, Tencent MyApp, 360, Wandoujia, Huawei, Meizu, and Vivo.
The Xiaomi App Store doesn&amp;rsquo;t allow it; submissions will be rejected, explicitly stating that simple shell APPs are not permitted.&lt;/p>
&lt;p>Since most significant stores currently support it, let&amp;rsquo;s continue to see how to implement it!&lt;/p>
&lt;h2 id="specific-strategy">
&lt;a class="heading-anchor-link" href="#specific-strategy">Specific Strategy&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="specific-strategy"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Initialize Cordova project
Please refer to the official doc for initializing the development environment and details. There is no need to repeat here, &lt;a href="https://cordova.apache.org/docs/en/latest/guide/cli/index.html" target="_blank" rel="noopener">click here&lt;/a>&lt;/li>
&lt;li>Install main plugins&lt;/li>
&lt;/ul>
&lt;ul>
&lt;li>&lt;code>$ cordova plugin add cordova-plugin-whitelist&lt;/code>&lt;/li>
&lt;li>&lt;code>$ cordova plugin add cordova-plugin-splashscreen&lt;/code>
This plugin mainly controls the APP launch screen settings&lt;/li>
&lt;li>&lt;code>$ cordova plugin add cordova-plugin-inappbrowser&lt;/code>
This plugin implements the opening of target web pages using the built-in browser in the app.&lt;/li>
&lt;/ul>
&lt;ul>
&lt;li>In the &lt;code>index.js&lt;/code> file, implement opening the corresponding site after the application starts.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Application Constructor
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">initialize&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;deviceready&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onDeviceReady&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// deviceready Event Handler
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">//
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Bind any cordova events here. Common events are:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// &amp;#39;pause&amp;#39;, &amp;#39;resume&amp;#39;, etc.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">onDeviceReady&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">receivedEvent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;deviceready&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Update DOM on a Received Event
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">receivedEvent&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">onDeviceReady&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// window.open = cordova.InAppBrowser.open;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kd">var&lt;/span> &lt;span class="nx">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;http://apache.org&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">target&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;_blank&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;location=no,toolbar=no&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cordova&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">InAppBrowser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">target&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">initialize&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="tips">
&lt;a class="heading-anchor-link" href="#tips">Tips&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="tips"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Regarding icons, apart from UI designers directly providing icons of corresponding sizes, I think using the Ionic service is also a good choice. After installing the Ionic framework, using &lt;code>ionic resources&lt;/code> to generate icons of corresponding sizes for each platform based on template images is also a convenient method. Then, copy the corresponding configuration items generated in config.xml.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>This approach mainly uses Cordova to achieve APP. In principle, it creates a browser but removes the browser interface, disguising it as an app.&lt;/p></description></item><item><title>What is Cordova</title><link>https://en.1991421.cn/2017/07/02/cordova/</link><pubDate>Sun, 02 Jul 2017 17:31:43 +0800</pubDate><guid>https://en.1991421.cn/2017/07/02/cordova/</guid><description>&lt;h2 id="what-is-cordova">
&lt;a class="heading-anchor-link" href="#what-is-cordova">What is Cordova&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-cordova"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For detailed information about Cordova and its history, I recommend checking Wikipedia and the official website, &lt;a href="https://en.wikipedia.org/wiki/Apache_Cordova" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/cordova-bot.png"
alt="Cordova"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>I&amp;rsquo;ll just give a brief introduction here. Cordova is a &lt;code>mobile application development framework&lt;/code> that enables developers to use &lt;code>CSS3&lt;/code>, &lt;code>HTML5&lt;/code>, and &lt;code>Javascript&lt;/code> to develop applications. It extends the capabilities of HTML and JavaScript on devices. Such applications are called &lt;code>hybrid&lt;/code> apps - they are neither truly native applications (because all layout rendering is not done through the platform&amp;rsquo;s native UI framework, but through Web Views), nor simple web-apps (they are packaged as APPs and have the ability to access native device APIs).&lt;/p>
&lt;h3 id="architecture-diagram">
&lt;a class="heading-anchor-link" href="#architecture-diagram">Architecture Diagram&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="architecture-diagram"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-10-163512.jpg"
alt="Architecture"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-is-the-relationship-between-ionic-and-cordova">
&lt;a class="heading-anchor-link" href="#what-is-the-relationship-between-ionic-and-cordova">What is the Relationship Between Ionic and Cordova&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-the-relationship-between-ionic-and-cordova"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/Ionic-logo.png"
alt="ionic"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
We generally don&amp;rsquo;t use Cordova directly to develop hybrid applications, but use Ionic instead. Ionic is an open-source SDK for building Hybrid-apps, providing tools and services for development. Ionic is built on top of AngularJS and Cordova, so it&amp;rsquo;s clear that Ionic and Cordova are not at the same level.&lt;/p>
&lt;h2 id="what-is-cordova-used-for">
&lt;a class="heading-anchor-link" href="#what-is-cordova-used-for">What is Cordova Used For&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-cordova-used-for"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Using Cordova, developers can use web development technologies to create app development, while leveraging the device&amp;rsquo;s underlying API access interfaces provided by Cordova to implement functions like camera, Bluetooth, etc. The existence of technologies like Ionic is to improve app development efficiency, providing us with a large number of component building blocks.&lt;/p></description></item><item><title>Angular 5: The Countdown Begins</title><link>https://en.1991421.cn/2017/07/01/angular5/</link><pubDate>Sat, 01 Jul 2017 13:07:08 +0800</pubDate><guid>https://en.1991421.cn/2017/07/01/angular5/</guid><description>&lt;p>&lt;a href="https://jaxenter.com/road-to-angular-5-133253.html" target="_blank" rel="noopener">Original English article&lt;/a>
We don&amp;rsquo;t know much about Angular&amp;rsquo;s next version, but we now know it will be officially released in October.
&lt;figure class="image-figure">
&lt;img
src="http://or0g12e5e.bkt.clouddn.com/QQ20170701-131026@2x.png"
alt="Angular Schedule"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
Official schedule &lt;a href="https://github.com/angular/angular/blob/master/docs/RELEASE_SCHEDULE.md" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">ng5 will be a better ng, and you&amp;#39;ll be able to use it more easily
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>One of the changes ng4 brought was smaller size and faster speed in AOT compilation mode. Their goal is to reduce component-generated code size by 60%.
ng5 will be even better. Minar guarantees that ng5 will be smaller and faster than ng4. The update from ng4 to ng5 will be smooth, and compilation will be simpler. Because the difference between JIT and AOT compilation is frustrating. The latter will become the default, reducing conflicts.&lt;/p>
&lt;h2 id="long-term-support-lts">
&lt;a class="heading-anchor-link" href="#long-term-support-lts">Long-Term Support (LTS)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="long-term-support-lts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;figure class="image-figure">
&lt;img
src="http://or0g12e5e.bkt.clouddn.com/long-term-supported.png"
alt="LTS"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>Minar revealed that all Google applications using ng&amp;rsquo;s latest pre-release versions and their teams are very satisfied with ng&amp;rsquo;s stability. Of course, except for those not using it, those using the latest minor versions will benefit. The answer is &lt;code>Long-Term Support&lt;/code>.
ng4 will be the first &lt;code>LTS&lt;/code> version. For the next 6 months, the Angular team will be active in releasing new versions and fixing bugs.
In October, V4 will enter a long-term state where only critical bug fixes and security patches will be merged and released.&lt;/p>
&lt;p>More information about ng5 will be revealed at &lt;code>ng-conf 2017&lt;/code>. Visit their &lt;a href="https://www.youtube.com/playlist?list=PLOETEcp3DkCoS_2cW205cfRGl-Xp5jw4K" target="_blank" rel="noopener">YouTube channel&lt;/a>&lt;/p></description></item><item><title>Git Configuration - Using Corresponding SSH Keys for Multiple Git Services</title><link>https://en.1991421.cn/2017/06/05/gitgitssh-key/</link><pubDate>Mon, 05 Jun 2017 22:52:54 +0800</pubDate><guid>https://en.1991421.cn/2017/06/05/gitgitssh-key/</guid><description>&lt;blockquote>
&lt;p>In actual development, encountered this problem: company service uses GitLab, personal side projects use GitHub, both have different accounts. For example, my company email is &lt;a href="mailto:he@company.com">he@company.com&lt;/a>, GitHub is personal email &lt;a href="mailto:he@1991421.cn">he@1991421.cn&lt;/a>. I don&amp;rsquo;t want to use the same Key, hoping to use corresponding authentication Keys for different Git services. After searching online, the method is as follows.&lt;/p>
&lt;/blockquote>
&lt;h2 id="configuration-steps">
&lt;a class="heading-anchor-link" href="#configuration-steps">Configuration Steps&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="configuration-steps"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>Generate SSH-key&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> cd ~/.ssh/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> # Generate for GitHub usage, use default name by pressing enter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> $ ssh-keygen -t rsa -C &amp;#34;he@1991421.cn&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> # Generate for company GitLab usage, rename to id_rsa_company
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> $ ssh-keygen -t rsa -C &amp;#34;he@1991421.cn&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This way both keys are generated separately, no conflict&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Paste public key to corresponding platform
&lt;code>cat id_rsa.pub&lt;/code>
&lt;code>cat id_rsa_company.pub&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Configure config
Execute &lt;code>touch config&lt;/code>, create configuration file
Paste the following configuration into it&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># company-GitLab
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Host compay.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IdentityFile ~/.ssh/id_rsa_company
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># GitHub
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Host *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">IdentityFile ~/.ssh/id_rsa
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre>&lt;code>\* means match all others
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>Test if successful&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># GitHub
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh -T git@GitHub.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Internal network GitLab
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh -T git@192.168.1.150
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>No error means OK. This way you can normally pull repositories and submit code.&lt;/p>
&lt;h2 id="usernameemail">
&lt;a class="heading-anchor-link" href="#usernameemail">Username/Email&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usernameemail"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Token differentiation only solves authentication permissions, but the commit username and email will still be default global configuration. If you want to personalize, need to execute the following commands in the repository.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ git config [--global] user.name &amp;#34;[name]&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git config [--global] user.email &amp;#34;[email address]&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you want to automatically use one set of configuration for company repositories and another set for personal projects, so you don&amp;rsquo;t have to manually set it every time you pull a repository. For specific operations, you can &lt;a href="https://1991421.cn/2019/07/06/115de663/" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>SSH login, prompts permission denied
Ensure password, configuration are correct. If still prompting, the possibility is actually in permissions. Make sure &lt;code>.ssh&lt;/code> folder permissions, recommend directly 777&lt;/li>
&lt;/ul></description></item><item><title>Ionic Android Signing and Build</title><link>https://en.1991421.cn/2017/05/30/92816972/</link><pubDate>Tue, 30 May 2017 18:37:13 +0800</pubDate><guid>https://en.1991421.cn/2017/05/30/92816972/</guid><description>&lt;h3 id="add-platform">
&lt;a class="heading-anchor-link" href="#add-platform">Add platform&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add-platform"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">ionic cordova platform add android
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="create-keystore">
&lt;a class="heading-anchor-link" href="#create-keystore">Create keystore&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-keystore"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">keytool&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">genkey&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">v&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">keystore&lt;/span> &lt;span class="n">he&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">release&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">keystore&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">alias&lt;/span> &lt;span class="n">he&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">keyalg&lt;/span> &lt;span class="n">RSA&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">keysize&lt;/span> &lt;span class="mi">2048&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">validity&lt;/span> &lt;span class="mi">10000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="create-signing-config">
&lt;a class="heading-anchor-link" href="#create-signing-config">Create signing config&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="create-signing-config"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Create &lt;code>release-signing.properties&lt;/code> under &lt;code>platforms/android&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">key.store=&amp;lt;YOUR_KEYSTORE&amp;gt;.keystore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">key.store.password=&amp;lt;YOUR_KEYSTORE password&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">key.alias=&amp;lt;YOUR_ALIAS&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">key.alias.password=&amp;lt;YOUR_ALIAS password&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="build-command">
&lt;a class="heading-anchor-link" href="#build-command">Build command&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="build-command"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>ionic cordova build android --prod --release&lt;/code>
You will see &lt;code>android-release.apk&lt;/code> under &lt;code>/platforms/android/build/outputs/apk&lt;/code>.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/cordova-build-android.png"
alt="cordova-build-android"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>You can also sign the APK manually from the command line, but it is more cumbersome. I recommend the automated signing above.&lt;/p>
&lt;p>Reference: &lt;a href="http://ionichelper.forlearning.net/ionic-2-building-a-release-android-apk/" target="_blank" rel="noopener">Ionic 2: Building A Release Android APK&lt;/a>&lt;/p></description></item><item><title>How to Use Ionic Native Plugins in the Browser (Step-by-Step Guide)</title><link>https://en.1991421.cn/2017/05/30/ionic/</link><pubDate>Tue, 30 May 2017 16:05:25 +0800</pubDate><guid>https://en.1991421.cn/2017/05/30/ionic/</guid><description>&lt;p>&lt;a href="https://ionicframework.com/docs/native/browser.html" target="_blank" rel="noopener">Official doc&lt;/a>&lt;/p>
&lt;h2 id="using-native-plugins-in-the-browser">
&lt;a class="heading-anchor-link" href="#using-native-plugins-in-the-browser">Using native plugins in the browser&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="using-native-plugins-in-the-browser"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Ionic Native has 130+ mobile SDK plugins, making it possible to build powerful Ionic apps.&lt;/p>
&lt;p>Historically, testing these native plugins in the browser was difficult. Developers had to test on physical devices or emulators, which is slow.&lt;/p>
&lt;p>Ionic Native 3.0 allows developers to mock these plugins in the browser by overriding classes. You can provide test data or access native APIs like HealthKit.&lt;/p>
&lt;p>This means many Ionic apps can be fully built in the browser without deploying to a device or simulator. A huge speed boost.&lt;/p>
&lt;h2 id="mocking-plugins">
&lt;a class="heading-anchor-link" href="#mocking-plugins">Mocking plugins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="mocking-plugins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>To use Ionic native plugins in the browser, extend the plugin class and override some methods.&lt;/p>
&lt;p>Let&amp;rsquo;s mock the &lt;code>Camera&lt;/code> plugin to return an image.&lt;/p>
&lt;p>First import &lt;code>Camera&lt;/code> in the root module.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">import { Camera } from &amp;#39;@ionic-native/camera&amp;#39;;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then create a class that extends &lt;code>Camera&lt;/code> and mocks an implementation.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">providers: [
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> { provide: Camera, useClass: CameraMock }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Below is a full example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">NgModule&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">ErrorHandler&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@angular/core&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">BrowserModule&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@angular/platform-browser&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">IonicApp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">IonicModule&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">IonicErrorHandler&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;ionic-angular&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">MyApp&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./app.component&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">HomePage&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../pages/home/home&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Camera&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;@ionic-native/camera&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">class&lt;/span> &lt;span class="nx">CameraMock&lt;/span> &lt;span class="kr">extends&lt;/span> &lt;span class="nx">Camera&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">getPicture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">options&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">reject&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;BASE_64_ENCODED_DATA_GOES_HERE&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">@NgModule&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">declarations&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">MyApp&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">HomePage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">imports&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">BrowserModule&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">IonicModule&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forRoot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">MyApp&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">bootstrap&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">IonicApp&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">entryComponents&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">MyApp&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">HomePage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">providers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nx">provide&lt;/span>: &lt;span class="kt">ErrorHandler&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useClass&lt;/span>: &lt;span class="kt">IonicErrorHandler&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nx">provide&lt;/span>: &lt;span class="kt">Camera&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">useClass&lt;/span>: &lt;span class="kt">CameraMock&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">AppModule&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Ionic CLI v3 Release</title><link>https://en.1991421.cn/2017/05/21/c0bfdce/</link><pubDate>Sun, 21 May 2017 16:29:06 +0800</pubDate><guid>https://en.1991421.cn/2017/05/21/c0bfdce/</guid><description>&lt;p>&lt;a href="https://blog.ionic.io/announcing-ionic-cli-v3/" target="_blank" rel="noopener">Original post&lt;/a>
Hello everyone, we are excited to announce that Ionic CLI v3 is now available!
Since we released the CLI v3 beta and the Easter egg, we saw many early beta testers successfully use it in their Ionic projects. They provided tons of feedback and had chances to win prizes. In fact, many of them found the Easter egg within hours. Recently, at our hackathon, developers became Ionic Jedi Hacksters and we got even more feedback.
(&lt;a href="http://blog.ionic.io/become-an-ionic-jedi-hackster/" target="_blank" rel="noopener">Hackathon results here&lt;/a>)
Beyond the version change, what makes this CLI special? Here are key highlights.&lt;/p>
&lt;h2 id="speed--guidance">
&lt;a class="heading-anchor-link" href="#speed--guidance">Speed + guidance&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="speed--guidance"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>You may notice the install is much faster, partly because we eliminated 90MB+ dependencies and thousands of lines of old code. Now the CLI takes less space and installs faster. Speed and performance are major goals.&lt;/p>
&lt;p>We also want to provide more help, guidance, and feedback. Many commands now provide interactive info. The CLI tries to be useful when problems arise. The help system has been improved. Just add &lt;code>--help&lt;/code> to any command for detailed info and parameters. We also provide examples, e.g. try &lt;code>ionic start --help&lt;/code>.&lt;/p>
&lt;h2 id="plugins">
&lt;a class="heading-anchor-link" href="#plugins">Plugins&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="plugins"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>We took a different approach: instead of bundling everything, we moved non-essential commands into plugins. This lets projects add features as needed while keeping the core small.&lt;/p>
&lt;p>For the first v3 release, there are four official plugins:&lt;/p>
&lt;p>@ionic/cli-plugin-ionic-angular - build tools and generators
@ionic/cli-plugin-ionic1 - CLI for ionic1 projects
@ionic/cli-plugin-cordova - essential for ionic/cordova projects
@ionic/cli-plugin-proxy - proxy plugin&lt;/p>
&lt;p>In beta, a common question was &amp;ldquo;why commands changed&amp;rdquo;. For example, &lt;code>ionic build&lt;/code> is now &lt;code>ionic cordova build&lt;/code>. We felt it was necessary because developers are building desktop, PWA, and other Ionic apps. To distinguish these, we made &lt;a href="https://docs.google.com/document/d/1r8nTAaJ5hLIJ1DCwBozU-JGV480Du0xCMIg2dj3JRQo/edit" target="_blank" rel="noopener">this list&lt;/a>.&lt;/p>
&lt;h3 id="get-started">
&lt;a class="heading-anchor-link" href="#get-started">Get started&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="get-started"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Make sure you have Node 6+ and npm 3+.&lt;/p>
&lt;p>Uninstall old CLI and install new CLI globally:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm uninstall -g ionic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -g ionic@latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In your Ionic project, make sure you have a standard structure. For example, &lt;code>ionic info&lt;/code> checks the project type and recommends plugins. If you run &lt;code>ionic cordova&lt;/code>, it prompts to install the cordova plugin. If you run &lt;code>ionic --help&lt;/code>, you&amp;rsquo;ll see all commands.
For ionic/cordova apps, you need the cordova plugin (@ionic/cli-plugin-cordova) and a project plugin (@ionic/cli-plugin-ionic-angular or @ionic/cli-plugin-ionic1).&lt;/p>
&lt;p>For ionic angular:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev --save-exact @ionic/cli-plugin-ionic-angular@latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install --save-dev --save-exact @ionic/cli-plugin-cordova@latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For ionic 1:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">npm install --save-dev --save-exact @ionic/cli-plugin-ionic1@latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install --save-dev --save-exact @ionic/cli-plugin-cordova@latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>CLI will occasionally remind you to update these plugins.&lt;/p>
&lt;h2 id="known-issues">
&lt;a class="heading-anchor-link" href="#known-issues">Known issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="known-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>For CLI v3, we are working on these:&lt;/p>
&lt;ul>
&lt;li>&lt;code>ionic start&lt;/code> still takes a long time to download dependencies. (#2231)&lt;/li>
&lt;li>&lt;code>ionic start&lt;/code> does not yet support options like GitHub repos, zip URLs, or Ionic author&amp;rsquo;s projects. (#1989)&lt;/li>
&lt;li>&lt;code>ionic cordova&lt;/code> writes 4 spaces to &lt;code>config.xml&lt;/code>, which can warn for existing apps. We write &lt;code>config.xml&lt;/code> for resource generation and live reload.&lt;/li>
&lt;li>For ionic1, some gulp hooks do not fire.&lt;/li>
&lt;li>On Android devices, upgrading to CLI v3 cannot correctly extract on deploy.&lt;/li>
&lt;/ul>
&lt;p>For the full upgrade list, see &lt;a href="https://github.com/driftyco/ionic-cli/blob/master/CHANGELOG.md#upgrading-from-cli-2" target="_blank" rel="noopener">CHANGELOG.md&lt;/a>.
For more docs, see &lt;a href="https://github.com/driftyco/ionic-cli/blob/master/README.md" target="_blank" rel="noopener">README.md&lt;/a>.&lt;/p>
&lt;p>Questions or feedback? Create an issue in the &lt;a href="https://github.com/driftyco/ionic-cli" target="_blank" rel="noopener">repo&lt;/a>.
Thanks to all beta testers, especially those who contributed issues, comments, and PRs.&lt;/p></description></item><item><title>Common Linux Commands Reference</title><link>https://en.1991421.cn/2017/05/15/linux/</link><pubDate>Mon, 15 May 2017 15:40:41 +0800</pubDate><guid>https://en.1991421.cn/2017/05/15/linux/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-19-040639.jpg"
alt="terminal"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>Navigating the command-line interface is essential for understanding and managing Linux systems. Here is a curated summary of commands I use most frequently in my daily work.&lt;/p>
&lt;/blockquote>
&lt;h2 id="system-information">
&lt;a class="heading-anchor-link" href="#system-information">System Information&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="system-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">uname -a &lt;span class="c1"># Display kernel version, OS, and CPU architecture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">head -n &lt;span class="m">1&lt;/span> /etc/issue &lt;span class="c1"># View operating system version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">env &lt;span class="c1"># List all environment variables&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check CentOS version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat /etc/redhat-release
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># CPU details&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat /proc/cpuinfo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Memory details&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat /proc/meminfo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Update system timezone&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">timedatectl set-timezone Asia/Shanghai
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="process-monitoring">
&lt;a class="heading-anchor-link" href="#process-monitoring">Process Monitoring&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="process-monitoring"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Search for a specific process using ps and grep&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ ps aux &lt;span class="p">|&lt;/span> grep &amp;lt;process_name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>&lt;code>ps aux&lt;/code> Output Columns:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>USER&lt;/strong>: Process owner&lt;/li>
&lt;li>&lt;strong>PID&lt;/strong>: Process ID&lt;/li>
&lt;li>&lt;strong>%CPU&lt;/strong>: CPU usage percentage&lt;/li>
&lt;li>&lt;strong>%MEM&lt;/strong>: Memory usage percentage&lt;/li>
&lt;li>&lt;strong>VSZ&lt;/strong>: Virtual memory size&lt;/li>
&lt;li>&lt;strong>RSS&lt;/strong>: Resident Set Size (physical memory)&lt;/li>
&lt;li>&lt;strong>TTY&lt;/strong>: Terminal type&lt;/li>
&lt;li>&lt;strong>STAT&lt;/strong>: Process status:
&lt;ul>
&lt;li>&lt;strong>D&lt;/strong>: Uninterruptible sleep (usually IO)&lt;/li>
&lt;li>&lt;strong>R&lt;/strong>: Running&lt;/li>
&lt;li>&lt;strong>S&lt;/strong>: Sleeping&lt;/li>
&lt;li>&lt;strong>T&lt;/strong>: Stopped or traced&lt;/li>
&lt;li>&lt;strong>Z&lt;/strong>: Zombie process&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>START&lt;/strong>: Process start time&lt;/li>
&lt;li>&lt;strong>TIME&lt;/strong>: Total CPU time used&lt;/li>
&lt;li>&lt;strong>COMMAND&lt;/strong>: The command that started the process&lt;/li>
&lt;/ul>
&lt;h2 id="account-management">
&lt;a class="heading-anchor-link" href="#account-management">Account Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="account-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Change the current user&amp;#39;s password&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">passwd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Show who is logged on and what they are doing&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">w
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="file-and-directory-management">
&lt;a class="heading-anchor-link" href="#file-and-directory-management">File and Directory Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="file-and-directory-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="permissions-and-attributes">
&lt;a class="heading-anchor-link" href="#permissions-and-attributes">Permissions and Attributes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="permissions-and-attributes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List file attributes, owners, and groups&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ls -l
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Change file group&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chgrp &lt;span class="o">[&lt;/span>-R&lt;span class="o">]&lt;/span> groupname filename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Change file owner (and optionally the group)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chown &lt;span class="o">[&lt;/span>-R&lt;span class="o">]&lt;/span> ownername filename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chown &lt;span class="o">[&lt;/span>-R&lt;span class="o">]&lt;/span> owner:group filename
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="basic-file-operations">
&lt;a class="heading-anchor-link" href="#basic-file-operations">Basic File Operations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="basic-file-operations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove a file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm filename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove a directory and its contents recursively&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rm -rf dirname
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Find and delete specific files/folders&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">find . -name &lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> xargs rm -rf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Copy a file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cp config.conf config.conf.bak
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Rename or move a file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mv old.txt new.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Move all files from a subdirectory to the current directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mv ./temp/deploy/* .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List all files (including hidden) in long format&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ls -Al
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List a specific range of files (e.g., 101st to 200th)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ls &lt;span class="p">|&lt;/span> sort -n &lt;span class="p">|&lt;/span> head -200 &lt;span class="p">|&lt;/span> tail -100
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="links-ln">
&lt;a class="heading-anchor-link" href="#links-ln">Links (ln)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="links-ln"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The &lt;code>ln&lt;/code> command creates links between files. Use &lt;code>-s&lt;/code> for symbolic (soft) links.
&lt;code>ln -s /path/to/source /path/to/destination&lt;/code>&lt;/p>
&lt;h3 id="archives-ziptar">
&lt;a class="heading-anchor-link" href="#archives-ziptar">Archives (zip/tar)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="archives-ziptar"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Extract a zip archive&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">unzip archive.zip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create a compressed tarball&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tar zcvf archive.tar.gz dirname/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Extract a tarball&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">tar zxvf archive.tar.gz
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="text-editing-vivim">
&lt;a class="heading-anchor-link" href="#text-editing-vivim">Text Editing (vi/vim)&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="text-editing-vivim"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Vim has three main modes: Command, Insert, and Last Line.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Command Mode&lt;/strong>: The default mode. Use &lt;code>i&lt;/code> to enter Insert mode, &lt;code>x&lt;/code> to delete a character, and &lt;code>:&lt;/code> to enter Last Line mode.&lt;/li>
&lt;li>&lt;strong>Insert Mode&lt;/strong>: For typing text. Press &lt;code>ESC&lt;/code> to return to Command mode.&lt;/li>
&lt;li>&lt;strong>Last Line Mode&lt;/strong>: Type &lt;code>:&lt;/code> followed by commands:
&lt;ul>
&lt;li>&lt;code>w&lt;/code>: Save (write)&lt;/li>
&lt;li>&lt;code>q&lt;/code>: Quit&lt;/li>
&lt;li>&lt;code>wq&lt;/code>: Save and quit&lt;/li>
&lt;li>&lt;code>q!&lt;/code>: Quit without saving&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="services-and-system-management">
&lt;a class="heading-anchor-link" href="#services-and-system-management">Services and System Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="services-and-system-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="chkconfig">
&lt;a class="heading-anchor-link" href="#chkconfig">chkconfig&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="chkconfig"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Used to manage services at different runlevels (CentOS 6 and earlier).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">chkconfig --list &lt;span class="c1"># List all system services&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chkconfig mysqld on &lt;span class="c1"># Enable a service at boot&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="service--systemctl">
&lt;a class="heading-anchor-link" href="#service--systemctl">service / systemctl&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="service--systemctl"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Used to manage system services.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">service nginx start &lt;span class="c1"># Start a service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">systemctl status nginx &lt;span class="c1"># Check service status (modern Linux)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="network-and-http">
&lt;a class="heading-anchor-link" href="#network-and-http">Network and HTTP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="network-and-http"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Download a file or view source code&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl https://example.com &amp;gt;&amp;gt; index.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Send a cookie with a request&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl --cookie &lt;span class="s2">&amp;#34;session=123&amp;#34;&lt;/span> https://example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="ssh">
&lt;a class="heading-anchor-link" href="#ssh">SSH&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ssh"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Local port forwarding (tunneling)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh -p &lt;span class="m">22&lt;/span> -L &amp;lt;localPort&amp;gt;:localhost:&amp;lt;serverPort&amp;gt; user@remote_host
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Execute a command on a remote server (using sshpass for automated scripts)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sshpass -p &lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span> ssh user@host &lt;span class="s2">&amp;#34;ls -la&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="searching-and-environment">
&lt;a class="heading-anchor-link" href="#searching-and-environment">Searching and Environment&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="searching-and-environment"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Find the absolute path of a command&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">which python
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Display a specific environment variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="nv">$NODE_ENV&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Set an environment variable for the current session&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">NODE_ENV&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Remove an environment variable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">unset&lt;/span> NODE_ENV
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note: To make an environment variable permanent, add it to &lt;code>/etc/profile&lt;/code> or &lt;code>~/.bashrc&lt;/code>.&lt;/em>&lt;/p>
&lt;h2 id="disk-and-hardware-management">
&lt;a class="heading-anchor-link" href="#disk-and-hardware-management">Disk and Hardware Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="disk-and-hardware-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="df--free">
&lt;a class="heading-anchor-link" href="#df--free">df / free&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="df--free"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View disk space in human-readable format&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">df -h
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View memory usage in MB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">free -m
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="lsof">
&lt;a class="heading-anchor-link" href="#lsof">lsof&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="lsof"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check which process is using a specific port&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lsof -i:8080
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="kill">
&lt;a class="heading-anchor-link" href="#kill">kill&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="kill"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Forcefully terminate a process by PID&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">kill&lt;/span> -9 &amp;lt;pid&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="troubleshooting">
&lt;a class="heading-anchor-link" href="#troubleshooting">Troubleshooting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="troubleshooting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="passwordless-ssh-login">
&lt;a class="heading-anchor-link" href="#passwordless-ssh-login">Passwordless SSH Login&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="passwordless-ssh-login"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ol>
&lt;li>Generate keys on your local machine: &lt;code>ssh-keygen&lt;/code>.&lt;/li>
&lt;li>Copy the content of &lt;code>~/.ssh/id_rsa.pub&lt;/code> to the target server&amp;rsquo;s &lt;code>~/.ssh/authorized_keys&lt;/code> file.&lt;/li>
&lt;li>Ensure the &lt;code>.ssh&lt;/code> directory has &lt;code>700&lt;/code> permissions and &lt;code>authorized_keys&lt;/code> has &lt;code>600&lt;/code>.&lt;/li>
&lt;/ol>
&lt;h3 id="host-key-verification-failed">
&lt;a class="heading-anchor-link" href="#host-key-verification-failed">&amp;ldquo;Host key verification failed&amp;rdquo;&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="host-key-verification-failed"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If a server&amp;rsquo;s fingerprint has changed (e.g., after a reinstallation), remove the old entry from your local &lt;code>~/.ssh/known_hosts&lt;/code> file and reconnect.&lt;/p>
&lt;h2 id="resources">
&lt;a class="heading-anchor-link" href="#resources">Resources&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="resources"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://man.linuxde.net/" target="_blank" rel="noopener">Linux Command Manual (Chinese)&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Webpack Build: File Hash Unchanged Despite Content Changes — Analysis and Fix</title><link>https://en.1991421.cn/2017/05/10/webpackhash/</link><pubDate>Wed, 10 May 2017 23:08:11 +0800</pubDate><guid>https://en.1991421.cn/2017/05/10/webpackhash/</guid><description>&lt;h3 id="while-bundling-an-angular-4-app-with-webpack-2-i-found-two-entry-files-whose-output-hash-didnt-change">
&lt;a class="heading-anchor-link" href="#while-bundling-an-angular-4-app-with-webpack-2-i-found-two-entry-files-whose-output-hash-didnt-change">While bundling an Angular 4 app with webpack 2, I found two entry files whose output hash didn’t change.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="while-bundling-an-angular-4-app-with-webpack-2-i-found-two-entry-files-whose-output-hash-didnt-change"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Files:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#39;common&amp;#39;: &amp;#39;./node_modules/moment/moment.js&amp;#39;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#39;polyfills&amp;#39;: `./client/${platform}/polyfills.browser.ts`,
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="these-two-entries-serve-momentjs-and-browser-compatibility-for-a-spa-my-webpack-config-for-outputs-looked-like-this">
&lt;a class="heading-anchor-link" href="#these-two-entries-serve-momentjs-and-browser-compatibility-for-a-spa-my-webpack-config-for-outputs-looked-like-this">These two entries serve Moment.js and browser compatibility for a SPA. My Webpack config for outputs looked like this:&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="these-two-entries-serve-momentjs-and-browser-compatibility-for-a-spa-my-webpack-config-for-outputs-looked-like-this"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"> /**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * Options affecting the output of the compilation.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * See: http://webpack.github.io/docs/configuration.html#output
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> output: {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * The output directory as absolute path (required).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * See: http://webpack.github.io/docs/configuration.html#output-path
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> path: helpers.root(&amp;#39;dist&amp;#39;),
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * Specifies the name of each output file on disk.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * IMPORTANT: You must not specify an absolute path here!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * See: http://webpack.github.io/docs/configuration.html#output-filename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> filename: &amp;#39;[name].[chunkhash].bundle.js&amp;#39;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * The filename of the SourceMaps for the JavaScript files.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * They are inside the output.path directory.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * See: http://webpack.github.io/docs/configuration.html#output-sourcemapfilename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sourceMapFilename: &amp;#39;[name].[chunkhash].bundle.map&amp;#39;,
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> /**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * The filename of non-entry chunks as relative path
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * inside the output.path directory.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> * See: http://webpack.github.io/docs/configuration.html#output-chunkfilename
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> */
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> chunkFilename: &amp;#39;[id].[chunkhash].chunk.js&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> },
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="after-analysis-the-entry-bundles-hashes-didnt-change-but-their-contents-did-because-of-references-like-webpackjsonp-consider-a-scenario-where-indexhtml-isnt-cached-and-it-references-common-by-a-fixed-file-name-pattern-if-commons-hash-doesnt-change-the-client-may-keep-using-the-cached-bundle-while-chunk-files-are-new-causing-runtime-errors">
&lt;a class="heading-anchor-link" href="#after-analysis-the-entry-bundles-hashes-didnt-change-but-their-contents-did-because-of-references-like-webpackjsonp-consider-a-scenario-where-indexhtml-isnt-cached-and-it-references-common-by-a-fixed-file-name-pattern-if-commons-hash-doesnt-change-the-client-may-keep-using-the-cached-bundle-while-chunk-files-are-new-causing-runtime-errors">After analysis: the entry bundles’ hashes didn’t change, but their contents did, because of references like &lt;code>webpackJsonp&lt;/code>. Consider a scenario where &lt;code>index.html&lt;/code> isn’t cached, and it references &lt;code>common&lt;/code> by a fixed file name pattern. If &lt;code>common&lt;/code>’s hash doesn’t change, the client may keep using the cached bundle while chunk files are new, causing runtime errors.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="after-analysis-the-entry-bundles-hashes-didnt-change-but-their-contents-did-because-of-references-like-webpackjsonp-consider-a-scenario-where-indexhtml-isnt-cached-and-it-references-common-by-a-fixed-file-name-pattern-if-commons-hash-doesnt-change-the-client-may-keep-using-the-cached-bundle-while-chunk-files-are-new-causing-runtime-errors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Why did the content change but the hash stay the same? Here’s the explanation:&lt;/p>
&lt;ol>
&lt;li>Definitions
&lt;ul>
&lt;li>&lt;code>[hash]&lt;/code> is replaced by the hash of the entire compilation.&lt;/li>
&lt;li>&lt;code>[chunkhash]&lt;/code> is replaced by the hash of the specific chunk.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Usage scenarios
&lt;code>chunkhash&lt;/code> is computed from an individual chunk’s content. Changing one chunk affects only its own hash, not others.&lt;/li>
&lt;/ol>
&lt;p>This explains the issue: the &lt;code>polyfills&lt;/code> and &lt;code>common&lt;/code> entry sources didn’t change, so their &lt;code>chunkhash&lt;/code> stayed the same. But the overall build changed, which altered the generated bundle content (e.g., runtime/chunk references). Therefore, entry bundles are not ideal for &lt;code>chunkhash&lt;/code>; prefer using &lt;code>[hash]&lt;/code> for entry filenames instead.&lt;/p>
&lt;h3 id="references">
&lt;a class="heading-anchor-link" href="#references">References&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="references"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;ul>
&lt;li>&lt;a href="http://www.cnblogs.com/ihardcoder/p/5623411.html" target="_blank" rel="noopener">http://www.cnblogs.com/ihardcoder/p/5623411.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/erm0l0v/webpack-md5-hash/issues/7" target="_blank" rel="noopener">https://github.com/erm0l0v/webpack-md5-hash/issues/7&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Introducing Ionic v4</title><link>https://en.1991421.cn/2017/05/06/ionicv4/</link><pubDate>Sat, 06 May 2017 23:37:41 +0800</pubDate><guid>https://en.1991421.cn/2017/05/06/ionicv4/</guid><description>&lt;h2 id="is-there-ionic-v4">
&lt;a class="heading-anchor-link" href="#is-there-ionic-v4">Is there Ionic v4?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-there-ionic-v4"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The official repo created a v4 branch and published its vision. I translated the key points here. Original: &lt;a href="https://github.com/driftyco/ionic/blob/v4/README.md" target="_blank" rel="noopener">click here&lt;/a>&lt;/p>
&lt;h2 id="translation">
&lt;a class="heading-anchor-link" href="#translation">Translation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="translation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Ionic components aim for these goals:&lt;/p>
&lt;ul>
&lt;li>Users continue to build apps and components with Angular components&lt;/li>
&lt;li>Development and build workflows remain unchanged&lt;/li>
&lt;li>User‑visible changes are minimized&lt;/li>
&lt;li>Reduced build time&lt;/li>
&lt;li>Reduced startup time&lt;/li>
&lt;li>Async loading of Ionic components becomes default&lt;/li>
&lt;/ul>
&lt;p>For most parts, &lt;code>ionic-angular&lt;/code> will continue to work the same way, using all the same APIs. However, a small number of complex components like &lt;code>ion-badge&lt;/code> will use the standard Web Components v1 spec, which is already implemented in mainstream browsers.&lt;/p>
&lt;p>For browsers without Web Components support, polyfills will be added on demand, which is already implemented in v4.&lt;/p>
&lt;p>We will continue to maintain the v3 main branch. Fundamentally, v3 and v4 differ internally, but externally they are equivalent for users.&lt;/p>
&lt;h3 id="changes">
&lt;a class="heading-anchor-link" href="#changes">Changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;h3 id="ngmodule-updates">
&lt;a class="heading-anchor-link" href="#ngmodule-updates">@NgModule Updates&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ngmodule-updates"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>What&amp;rsquo;s great is that Angular already supports web components. To enable them, add CUSTOM_ELEMENTS_SCHEMA to the schemas property of @NgModule:&lt;/p>
&lt;p>import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from &amp;lsquo;@angular/core&amp;rsquo;;&lt;/p>
&lt;p>@NgModule({
&amp;hellip;
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}&lt;/p>
&lt;h3 id="ion-label-required">
&lt;a class="heading-anchor-link" href="#ion-label-required">ion-label Required&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="ion-label-required"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Previously an ion-label would automatically be added to ion-item if one wasn&amp;rsquo;t provided. Now an ion-label must always be added if the item displays text.&lt;/p>
&lt;ion-item>
&lt;ion-label>Item's Text!&lt;/ion-label>
&lt;/ion-item>
&lt;h2 id="future-goals">
&lt;a class="heading-anchor-link" href="#future-goals">Future goals&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="future-goals"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Ionic integrates the Web Components standard. One of our goals is to make Ionic components run easily in all major frameworks.&lt;/p></description></item><item><title>Git Common Commands</title><link>https://en.1991421.cn/2017/05/05/git-common-commands/</link><pubDate>Fri, 05 May 2017 23:22:37 +0800</pubDate><guid>https://en.1991421.cn/2017/05/05/git-common-commands/</guid><description>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-09-10-161721.jpg"
alt="What is git and why should I use it? - Quora"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>To work well with GitHub open source projects, you need to understand Git. So here I&amp;rsquo;m documenting some commands I&amp;rsquo;ve used in practice, to help myself remember them and hopefully assist some friends as well.
Remember the main commands for convenient operation, and just look up the rest when needed.
Using practical examples to illustrate some commands I&amp;rsquo;ve used in actual development.&lt;/p>
&lt;/blockquote>
&lt;h2 id="getting-and-creating-projects">
&lt;a class="heading-anchor-link" href="#getting-and-creating-projects">Getting and Creating Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-and-creating-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create an empty Git repository, or reinitialize an existing repository&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config &lt;span class="o">[&lt;/span>--global&lt;span class="o">]&lt;/span> user.name &lt;span class="s2">&amp;#34;alanhg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config &lt;span class="o">[&lt;/span>--global&lt;span class="o">]&lt;/span> user.email &lt;span class="s2">&amp;#34;i@xx.x&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git config --global --add push.default current
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;!-- more -->
&lt;h2 id="clone">
&lt;a class="heading-anchor-link" href="#clone">Clone&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="clone"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Clone repository&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone -b &lt;span class="nb">source&lt;/span> https://github.com/alanhe421/alfred-workflows
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Only pull specific branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --single-branch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Modify local folder mapping after pulling&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone -b &lt;span class="nb">source&lt;/span> https://github.com/alanhe421/alfred-workflows heqiang422
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="basic-snapshotting">
&lt;a class="heading-anchor-link" href="#basic-snapshotting">Basic Snapshotting&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="basic-snapshotting"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="add">
&lt;a class="heading-anchor-link" href="#add">Add&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="add"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git add .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="commit">
&lt;a class="heading-anchor-link" href="#commit">Commit&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="commit"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git commit -m &lt;span class="s1">&amp;#39;message&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Modify latest commit&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="reset">
&lt;a class="heading-anchor-link" href="#reset">Reset&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reset"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Undo staging area commit, revert one version&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git reset HEAD^
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Restore to a known state&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># git reset --hard&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="rm">
&lt;a class="heading-anchor-link" href="#rm">rm&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="rm"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Sometimes after adding certain files to git add for version tracking, you regret it. In this case, rm can solve the problem.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Stop tracking specified file, but the file remains in the working directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rm --cached &lt;span class="o">[&lt;/span>file&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Delete working directory file and put this deletion into the staging area&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rm &lt;span class="o">[&lt;/span>file1&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>file2&lt;span class="o">]&lt;/span> ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="restore">
&lt;a class="heading-anchor-link" href="#restore">restore&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="restore"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Restore from local repository, but keep modifications&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git restore --staged &lt;span class="o">[&lt;/span>file&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="branching-and-merging">
&lt;a class="heading-anchor-link" href="#branching-and-merging">Branching and Merging&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="branching-and-merging"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="branch">
&lt;a class="heading-anchor-link" href="#branch">Branch&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="branch"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># View local branches&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Delete local branch dev&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch -d dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Create new branch based on previous commit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch &amp;lt;branchName&amp;gt; &amp;lt;sha1-of-commit&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Establish tracking relationship between existing branch and specified remote branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch --set-upstream &lt;span class="o">[&lt;/span>branch&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>remote-branch&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Modify corresponding remote branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch -u origin/dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Rename local branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git branch &lt;span class="o">(&lt;/span>-m &lt;span class="p">|&lt;/span> -M&lt;span class="o">)&lt;/span> &lt;span class="o">[&lt;/span>&amp;lt;oldbranch&amp;gt;&lt;span class="o">]&lt;/span> &amp;lt;newbranch&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="checkout">
&lt;a class="heading-anchor-link" href="#checkout">Checkout&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="checkout"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Switch local branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git checkout &amp;lt;branchName&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Switch branch and modify remote branch to dev&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git checkout dev --track origin/dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="sharing-and-updating-projects">
&lt;a class="heading-anchor-link" href="#sharing-and-updating-projects">Sharing and Updating Projects&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="sharing-and-updating-projects"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="pull">
&lt;a class="heading-anchor-link" href="#pull">pull&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pull"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Pull latest code&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git pull
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="push">
&lt;a class="heading-anchor-link" href="#push">push&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="push"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Delete remote branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin --delete &amp;lt;branchName&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Push to main branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin &amp;lt;branchName&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="remote">
&lt;a class="heading-anchor-link" href="#remote">remote&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="remote"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List remote repository information, including URLs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add remote repository, supports multiple remote repository addresses&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote add &lt;span class="o">[&lt;/span>shortname&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>url&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Modify URL corresponding to remote repository&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote set-url origin git@github.com:username/repo.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="common-issues">
&lt;a class="heading-anchor-link" href="#common-issues">Common Issues&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="common-issues"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="git-fatal-pathspec-is-in-submodule">
&lt;a class="heading-anchor-link" href="#git-fatal-pathspec-is-in-submodule">Git: fatal: Pathspec is in submodule&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="git-fatal-pathspec-is-in-submodule"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Solution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> git rm --cached directory
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> git add directory
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="you-have-not-concluded-your-merge-merge_head-exists">
&lt;a class="heading-anchor-link" href="#you-have-not-concluded-your-merge-merge_head-exists">You have not concluded your merge (MERGE_HEAD exists).&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="you-have-not-concluded-your-merge-merge_head-exists"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git reset --merge
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="prompted-for-username-and-password-every-time-push">
&lt;a class="heading-anchor-link" href="#prompted-for-username-and-password-every-time-push">Prompted for username and password every time push&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="prompted-for-username-and-password-every-time-push"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-07-07-031614.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>
This happens because the remote repository is set up using HTTPS protocol. Change it to SSH.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check remote repository protocol&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote rm origin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git remote add origin git@github.com:alanhg/alanhe421.github.io.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="pull-is-not-possible-because-you-have-unmerged-files">
&lt;a class="heading-anchor-link" href="#pull-is-not-possible-because-you-have-unmerged-files">Pull is not possible because you have unmerged files.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="pull-is-not-possible-because-you-have-unmerged-files"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This happens because when pulling new code from upstream, it performs a merge. If there are unmerged files, it will show the above message. Either handle the conflict with &lt;code>git add -u, git commit&lt;/code>, or abandon local file modifications and execute &lt;code>git reset --hard FETCH_HEAD&lt;/code>.&lt;/p>
&lt;h3 id="import-partial-commits-from-one-branch-to-another">
&lt;a class="heading-anchor-link" href="#import-partial-commits-from-one-branch-to-another">Import partial commits from one branch to another&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="import-partial-commits-from-one-branch-to-another"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Sometimes we need to import some commits from one branch to another [these commits may not be the latest consecutive commits in that branch]. In this case, we can use cherry pick to solve it.
We record the commitID we want to import, checkout to the target branch, and execute &amp;lsquo;git cherry-pick commitID&amp;rsquo;. Note that there may be conflicts, handle them the same way as MR.&lt;/p>
&lt;p>Note that if we want to import N commits, after cherry picking, it will still be N commits.&lt;/p>
&lt;h3 id="unable-to-write-sha1-filename-permission-denied">
&lt;a class="heading-anchor-link" href="#unable-to-write-sha1-filename-permission-denied">unable to write sha1 filename Permission denied&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="unable-to-write-sha1-filename-permission-denied"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Recently, our team used &lt;a href="https://www.modelio.org" target="_blank" rel="noopener">Modelio&lt;/a> to edit UML, and when executing git add operation, it reported an error.&lt;/p>
&lt;p>The error message indicated permission issues, so the first solution that came to mind was to run the terminal as administrator, or modify file permissions in the .git directory with chmod 777. But neither method worked. Trying to add files individually, I found some worked, so I inferred that some files might be special. Could it be a file editing state issue? So I closed the running modelio that was being edited, and indeed it worked.&lt;/p>
&lt;p>I inferred that when software is editing files, some files get locked. The add operation would cause some file modifications, and these files cannot be edited, hence the error.&lt;/p>
&lt;h3 id="you-have-unstaged-changes">
&lt;a class="heading-anchor-link" href="#you-have-unstaged-changes">You have unstaged changes&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="you-have-unstaged-changes"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This error means there are modifications that haven&amp;rsquo;t been committed or stashed. What needs to be done is to git add, commit, or stash these modifications.&lt;/p>
&lt;h3 id="cannot-pull-with-rebase-you-have-unstaged-changesplease-commit-or-stash-them">
&lt;a class="heading-anchor-link" href="#cannot-pull-with-rebase-you-have-unstaged-changesplease-commit-or-stash-them">Cannot pull with rebase: You have unstaged changes.↵Please commit or stash them.&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="cannot-pull-with-rebase-you-have-unstaged-changesplease-commit-or-stash-them"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When executing &lt;code>git pull --rebase --autostash&lt;/code>, the above error is reported because there are some modifications that need to be committed or stashed, making it impossible to pull code normally.&lt;/p>
&lt;h3 id="gitmerging-status">
&lt;a class="heading-anchor-link" href="#gitmerging-status">Git:Merging status&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gitmerging-status"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>In IDEA, for example, when merging branches [merge sprint with featue/a], conflicts may occur. After handling conflicts, commit fails, and the branch status shows as Git:Merging sprit. At this point, executing &lt;code>git status&lt;/code> in the terminal shows the current status as &lt;code>sprint | merge&lt;/code>. What does this status mean? Actually, it means that after MR conflict resolution, &lt;code>automatic commit failed&lt;/code>. At this point, manual modification and resolution are needed, then re-execute commit. After successful submission, it will return to &lt;code>sprint&lt;/code>. Of course, you can also abandon MR with &lt;code>git merge --abort&lt;/code>.&lt;/p>
&lt;h3 id="the-rsa-host-key-for-githubcom-differs-from-the-key-for-the-ip-address">
&lt;a class="heading-anchor-link" href="#the-rsa-host-key-for-githubcom-differs-from-the-key-for-the-ip-address">the RSA host key for &amp;lsquo;github.com&amp;rsquo; differs from the key for the IP address&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="the-rsa-host-key-for-githubcom-differs-from-the-key-for-the-ip-address"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;blockquote>
&lt;p>Warning: the RSA host key for &amp;lsquo;github.com&amp;rsquo; differs from the key for the IP address &amp;lsquo;198.18.1.89&amp;rsquo;
Offending key for IP in /Users/xx/.ssh/known_hosts:27
Matching host key in /Users/xx/.ssh/known_hosts:1
Are you sure you want to continue connecting (yes/no)? ^C&lt;/p>
&lt;/blockquote>
&lt;p>When submitting, this message always appears. The solution is to open known_hosts and delete the target line content. Then resubmit.&lt;/p>
&lt;h4 id="important-details">Important Details&lt;/h4>&lt;ol>
&lt;li>MR itself is a commit, and the role of commit is to submit local changes to the staging area.&lt;/li>
&lt;li>Commit failure during MR only means commit failed. The MR code merge modifications are already completed. At this point, make modifications locally based on the commit error, then commit again.&lt;/li>
&lt;li>The final success marker of an MR is the successful submission of the post-MR commit, not including push. Push only pushes this MR submission upstream.&lt;/li>
&lt;li>Don&amp;rsquo;t easily modify commit messages, because Git recognizes this as an MR commit based on the header format of the commit message.&lt;/li>
&lt;/ol>
&lt;h3 id="shell-request-failed-on-channel-0">
&lt;a class="heading-anchor-link" href="#shell-request-failed-on-channel-0">shell request failed on channel 0&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="shell-request-failed-on-channel-0"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>&lt;code>vi /etc/security/limits.d/20-nproc.conf&lt;/code>&lt;/p>
&lt;p>Change &lt;code>4096&lt;/code> to a larger value, such as &lt;code>65535&lt;/code>, or set to &lt;code>unlimited&lt;/code> if no limit is desired.&lt;/p>
&lt;h3 id="git-clone-prompts-for-username-and-password">
&lt;a class="heading-anchor-link" href="#git-clone-prompts-for-username-and-password">git clone prompts for username and password&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="git-clone-prompts-for-username-and-password"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When cloning a repository using git protocol, and &lt;code>ssh -T test is normal&lt;/code> but still prompts for username and password, it&amp;rsquo;s because &lt;code>the communication protocol has been forcibly redirected&lt;/code>.&lt;/p>
&lt;p>Solution&lt;/p>
&lt;p>vi ~/.gitconfig, comment out &lt;code>url.https://git.xxx.com/.insteadof=git@xxx:&lt;/code>&lt;/p>
&lt;h3 id="fatal-fetch-pack-invalid-index-pack-output">
&lt;a class="heading-anchor-link" href="#fatal-fetch-pack-invalid-index-pack-output">fatal: fetch-pack: invalid index-pack output&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="fatal-fetch-pack-invalid-index-pack-output"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The reason is the repository is too large, so directly limit history to only pull the latest commit. &amp;ndash;depth implies &amp;ndash;single-branch, meaning the following command will only pull one branch.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git clone git@github.com:nodejs/node.git --depth&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you later want to pull the complete history, you need to execute&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git fetch --unshallow
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="reference-materials">
&lt;a class="heading-anchor-link" href="#reference-materials">Reference Materials&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="reference-materials"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://git-scm.com/docs" target="_blank" rel="noopener">Git Manual&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Implementing File Downloads with Express</title><link>https://en.1991421.cn/2017/03/29/expressjs/</link><pubDate>Wed, 29 Mar 2017 20:36:47 +0800</pubDate><guid>https://en.1991421.cn/2017/03/29/expressjs/</guid><description>&lt;p>Looking at the &lt;a href="http://expressjs.com/zh-cn/4x/api.html#res.download" target="_blank" rel="noopener">Express API&lt;/a>,
for file downloads, the simplest implementation method is as follows&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/report-12345.pdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;report.pdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Handle error, but keep in mind the response may be partially-sent
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// so check res.headersSent
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// decrement a download credit, etc.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>However, in actual use, a problem was discovered with this method - when downloading with Apple Safari browser, the file title would be automatically truncated, become garbled, or show question marks.&lt;/p>
&lt;p>At first, I was puzzled since Internet Explorer had no issues. After analyzing with Fiddler packet capture, I found that the response header information was problematic - there were duplicate filenames. Different browsers handle duplicate filenames differently, or rather, Safari has issues with duplicate filenames. The res.download approach is highly abstracted after all.&lt;/p>
&lt;p>In other words, avoid using such highly abstracted approaches. So how to solve this?&lt;/p>
&lt;p>Here&amp;rsquo;s an alternative approach that isn&amp;rsquo;t highly abstracted and manually writes response header information. After testing, Safari downloads work perfectly.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Hello, Earthlings Hello, Earthlings Hello, Earthlings Hello, Earthlings.pdf&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">filePath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">resolve&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;..&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;/static/pdf/test.pdf&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">mimetype&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Disposition&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;attachment; filename=&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filename&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;binary&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-type&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mimetype&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">filestream&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createReadStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filestream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Comparing the two approaches, Method 1 is much simpler than Method 2, but currently doesn&amp;rsquo;t support Safari and IE well. If using Method 2 directly, Edge will have issues, which then involves handling logic for different browsers with mixed Chinese and English filenames.&lt;/p>
&lt;p>So the best solution is to handle this based on request headers.
For browser detection, you can use the following library:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">parser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ua-parser-js&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">ua&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">parser&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">headers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;user-agent&amp;#39;&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;Edge&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Chrome&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Firefox&amp;#39;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">indexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">download&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">filename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Error occurred&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">logger&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">mimetype&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">mime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-type&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mimetype&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">ua&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;IE&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Disposition&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;attachment; filename=&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">encodeURIComponent&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filename&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="cm">/*else if (ua.browser.name == &amp;#39;Firefox&amp;#39;) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> res.setHeader(&amp;#39;Content-Disposition&amp;#39;, &amp;#39;attachment; filename*=&amp;#34;utf8\&amp;#39;\&amp;#39;&amp;#39; + encodeURIComponent(filename) + &amp;#39;&amp;#34;&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> } */&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* Safari and other non-mainstream browsers can only hope for the best */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setHeader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Content-Disposition&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;attachment; filename=&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filename&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;binary&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">filestream&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">fs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createReadStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">filePath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">filestream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">res&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="browser-support">
&lt;a class="heading-anchor-link" href="#browser-support">Browser Support&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="browser-support"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The above code has been tested and supports the following browsers:&lt;/p>
&lt;ul>
&lt;li>IE&lt;/li>
&lt;li>Chrome&lt;/li>
&lt;li>Firefox&lt;/li>
&lt;li>Edge&lt;/li>
&lt;li>360 (Fast &amp;amp; Compatible modes)&lt;/li>
&lt;li>QQ Browser (Fast &amp;amp; Compatible modes)&lt;/li>
&lt;/ul></description></item><item><title>Android Decompilation Explained (Beginner's Guide)</title><link>https://en.1991421.cn/2017/03/13/android/</link><pubDate>Mon, 13 Mar 2017 08:26:53 +0800</pubDate><guid>https://en.1991421.cn/2017/03/13/android/</guid><description>&lt;blockquote>
&lt;p>Due to work needs, I have learned about decompilation and gained some insights. Here is a summary.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-05-055903.jpg"
alt="Android"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h2 id="what-is-decompilation">
&lt;a class="heading-anchor-link" href="#what-is-decompilation">What is Decompilation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="what-is-decompilation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>APP development involves packaging project code into an APP. For Android, it is an APK package, which is finally installed on the user&amp;rsquo;s phone through the store or other channels.
Decompilation is the process of reverse engineering the APP package to retrieve all the original resources, such as project code.&lt;/p>
&lt;h3 id="is-it-feasible-for-android-and-ios">
&lt;a class="heading-anchor-link" href="#is-it-feasible-for-android-and-ios">Is it feasible for Android and iOS?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="is-it-feasible-for-android-and-ios"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>iOS is a closed environment, so the feasibility is almost zero unless there is a significant investment; you can consider it as zero.
Android still has some feasibility, although most current apps have code obfuscation and security reinforcements.&lt;/p>
&lt;h2 id="decompilation-process">
&lt;a class="heading-anchor-link" href="#decompilation-process">Decompilation Process&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="decompilation-process"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The decompilation discussed here is for &lt;code>Android&lt;/code>.&lt;/p>
&lt;h2 id="environment-preparation">
&lt;a class="heading-anchor-link" href="#environment-preparation">Environment Preparation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-preparation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>JAVA environment - recommended &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" rel="noopener">1.8+&lt;/a>&lt;/li>
&lt;li>IDE, recommended &lt;code>IntelliJ IDEA&lt;/code> or &lt;code>Android Studio&lt;/code>&lt;/li>
&lt;li>Unix environment, recommended &lt;code>Mac, Ubuntu&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="decompilation-tools">
&lt;a class="heading-anchor-link" href="#decompilation-tools">Decompilation Tools&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="decompilation-tools"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Tried some online tools &lt;a href="www.decompileandroid.com">decompileandroid&lt;/a>, &lt;a href="https://github.com/skylot/jadx" target="_blank" rel="noopener">jadx&lt;/a>, &lt;a href="https://github.com/dirkvranckaert/AndroidDecompiler" target="_blank" rel="noopener">AndroidDecompiler&lt;/a>,
and compared and recommended &lt;code>jadx&lt;/code>. Below is a basic introduction to using jadx.&lt;/p>
&lt;h2 id="getting-started">
&lt;a class="heading-anchor-link" href="#getting-started">Getting Started&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="getting-started"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="download-zip-package">
&lt;a class="heading-anchor-link" href="#download-zip-package">Download ZIP Package&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="download-zip-package"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Website: &lt;a href="https://github.com/skylot/jadx/releases" target="_blank" rel="noopener">click here&lt;/a>, download the latest zip package, and make sure not to choose the source code.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-04-145125.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="environment-variables">
&lt;a class="heading-anchor-link" href="#environment-variables">Environment Variables&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-variables"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Different platforms have different configurations.&lt;/p>
&lt;p>For example, on Mac, I configured it in &lt;code>cat ~/.bash_profile&lt;/code> for user-level environment variables. To take effect immediately, execute &lt;code>source ~/.bash_profile&lt;/code>.&lt;/p>
&lt;h3 id="run-decompilation-tool">
&lt;a class="heading-anchor-link" href="#run-decompilation-tool">Run Decompilation Tool&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="run-decompilation-tool"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Run the decompilation GUI tool&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">jadx-gui
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Click File—Open File, select the target APK, and wait for the tool to execute automatically. You will see that the tool successfully decompiles the project. You can choose to save all files to a certain place.&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-04-145724.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="open-the-project">
&lt;a class="heading-anchor-link" href="#open-the-project">Open the Project&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="open-the-project"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>The figure shows the project CODE decompiled from a certain app.
&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2018-01-04-145604.png"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;h3 id="analyze-code">
&lt;a class="heading-anchor-link" href="#analyze-code">Analyze CODE&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="analyze-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This depends on personal skills. Having learned JAVA and Android will help a lot.&lt;/p>
&lt;h2 id="beyond-the-code">
&lt;a class="heading-anchor-link" href="#beyond-the-code">Beyond the Code&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="beyond-the-code"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Besides the decompiled project code, you need to consider two points:&lt;/p>
&lt;h3 id="play-with-the-app">
&lt;a class="heading-anchor-link" href="#play-with-the-app">Play with the APP&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="play-with-the-app"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>It will be much easier to find the corresponding Activity and other libraries in the CODE through specific app operations, such as the post button.&lt;/p>
&lt;h3 id="set-up-an-emulator-proxy-network-and-capture-packets">
&lt;a class="heading-anchor-link" href="#set-up-an-emulator-proxy-network-and-capture-packets">Set up an Emulator, Proxy Network, and Capture Packets&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="set-up-an-emulator-proxy-network-and-capture-packets"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Apart from the APP itself, the APP needs to interact with the server. Therefore, using tools like Fiddler to capture packets for analysis can provide interface information and verify certain assumptions.
Most current HTTP requests are encrypted, i.e., HTTPS, which requires certificates to resolve. It is recommended to check relevant information.&lt;/p>
&lt;h2 id="at-the-end">
&lt;a class="heading-anchor-link" href="#at-the-end">At the end&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="at-the-end"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>Currently, in Android development, code obfuscation is done during packaging. So, even after shelling and decompiling, it doesn&amp;rsquo;t completely reveal the project&amp;rsquo;s situation, but it generally doesn&amp;rsquo;t affect readability.
In fact, decompiling some excellent apps, reading others&amp;rsquo; code, and understanding their overall design is a great way to learn.&lt;/p>
&lt;ul>
&lt;li>The front end is inherently transparent, so decompiling to learn is good. Do not directly steal others&amp;rsquo; work, which needs to be noted.&lt;/li>
&lt;/ul></description></item><item><title>How to Use Sessions in Express (Step-by-Step Guide)</title><link>https://en.1991421.cn/2016/10/16/sessionexpress/</link><pubDate>Sun, 16 Oct 2016 14:49:01 +0800</pubDate><guid>https://en.1991421.cn/2016/10/16/sessionexpress/</guid><description>&lt;blockquote>
&lt;p>HTTP is a stateless protocol. To maintain user state between the frontend and backend, sessions are one approach. How do we do this in Express? Read on.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/2018-06-21-035100.jpg"
alt=""
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Install the session middleware
&lt;code>npm i express-session --save&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Enable session configuration
Configure as follows in &lt;code>app.js&lt;/code>; here is the complete file:&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">express&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;express&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">conf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./config&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">routes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./routes/index&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;path&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">bodyParser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;body-parser&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">isDeveloping&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="s1">&amp;#39;development&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;development&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;express-session&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">enable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;trust proxy&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// trust first proxy
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">bodyParser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">());&lt;/span> &lt;span class="c1">// for parsing application/json
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">sessionConfig&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">secret&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Shh, its a secret!&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resave&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saveUninitialized&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">isDeveloping&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">RedisStore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;connect-redis&amp;#39;&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sessionConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">store&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RedisStore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">conf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">redis&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">sessionConfig&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// mount the router on the app
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">routes&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// serve static assets
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">static&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;/static&amp;#39;&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">isDeveloping&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kr">static&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;dist&amp;#39;&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;*&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sendFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">__dirname&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s1">&amp;#39;/dist/index.html&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">listen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">conf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">port&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;127.0.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`campus-server app listening on port &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">conf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">port&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">!`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>Update the session on user login&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/login&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">appUsers&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">password&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">password&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">userWithoutPassword&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{...&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="nx">userWithoutPassword&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">password&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">user&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">userWithoutPassword&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">user&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">userWithoutPassword&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">403&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">errorMessage&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Permission denied!&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>Log out and destroy the session&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">router&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/logout&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">destroy&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Could not log out.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">200&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">({});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="5">
&lt;li>Change the session storage backend
By default, sessions use &lt;code>MemoryStore&lt;/code>. When you deploy to production, you’ll see a warning like this:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Warning: connect.session() MemoryStore is not
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">designed for a production environment, as it will leak
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, I use Redis as the production-grade session store, so the server needs Redis installed: &lt;code>yum install -y redis&lt;/code>.
At the same time, the backend needs the corresponding middleware: &lt;code>npm install connect-redis --save&lt;/code>.
Looking back at the configuration above, this is why we only add the Redis configuration in production.&lt;/p></description></item><item><title>PM2 Introduction</title><link>https://en.1991421.cn/2016/09/19/pm2-intro/</link><pubDate>Mon, 19 Sep 2016 21:37:13 +0800</pubDate><guid>https://en.1991421.cn/2016/09/19/pm2-intro/</guid><description>&lt;p>Original English repository URL: &lt;a href="https://github.com/Unitech/pm2" target="_blank" rel="noopener">Click here&lt;/a>&lt;/p>
&lt;p>PM2 is a &lt;code>production-grade&lt;/code> process manager for Node.js applications with built-in load balancing. It ensures applications stay running continuously, enables zero-downtime restarts, and makes it easy to manage these system tasks.
Starting an application in production mode is very simple, like this:
&lt;code>$ pm2 start app.js&lt;/code>
PM2 works on Linux (stable), macOS (stable), and Windows (stable).&lt;/p>
&lt;h2 id="installing-pm2">
&lt;a class="heading-anchor-link" href="#installing-pm2">Installing PM2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="installing-pm2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;code>$ npm install pm2 -g&lt;/code>
The npm command comes with Node.js when you install it - &lt;a href="https://keymetrics.io/2015/02/03/installing-node-js-and-io-js-with-nvm/" target="_blank" rel="noopener">Install Node via NVM&lt;/a>&lt;/p>
&lt;h2 id="updating-pm2">
&lt;a class="heading-anchor-link" href="#updating-pm2">Updating PM2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="updating-pm2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install the latest version of PM2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ npm install pm2 -g
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Save the process list, exit the old PM2, and restore all processes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ pm2 update
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>PM2 updates seamlessly.&lt;/p>
&lt;h2 id="main-features">
&lt;a class="heading-anchor-link" href="#main-features">Main Features&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="main-features"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="command-reference">
&lt;a class="heading-anchor-link" href="#command-reference">Command Reference&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="command-reference"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Common commands&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">npm&lt;/span> &lt;span class="n">install&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">g&lt;/span> &lt;span class="c1"># Install PM2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span> &lt;span class="c1"># Start application, automatically restart if it crashes (Node)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">py&lt;/span> &lt;span class="c1"># (Python)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">npm&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="c1"># &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cluster mode (Node.js only)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="mi">4&lt;/span> &lt;span class="c1"># Cluster mode, start 4 application instances&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Load balancing based on network requests&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">reload&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="c1"># Zero-downtime restart&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">scale&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="mi">10&lt;/span> &lt;span class="c1"># Scale cluster app processes to 10&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Process monitoring&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">list&lt;/span> &lt;span class="c1"># List all applications started with PM2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">monit&lt;/span> &lt;span class="c1"># Display memory and CPU usage for each application&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">show&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Show all information about an application&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Log management&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">logs&lt;/span> &lt;span class="c1"># Display logs for all applications&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">logs&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Display logs for a specific application&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">logs&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="c1"># Display logs in JSON format&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">flush&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">reloadLogs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Process state management&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;api&amp;#34;&lt;/span> &lt;span class="c1"># Start application with name &amp;#34;api&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="mi">34&lt;/span> &lt;span class="c1"># Start application with parameter &amp;#34;-a 34&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">watch&lt;/span> &lt;span class="c1"># Restart application when files change&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">script&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sh&lt;/span> &lt;span class="c1"># Start bash script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="c1"># Start all applications declared in app.json file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">reset&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Reset application counters (restart counts)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">stop&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="c1"># Stop all applications&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">stop&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="c1"># Stop application with ID 0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">restart&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="c1"># Restart all applications&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">gracefulReload&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="c1"># Graceful reload of all applications in cluster mode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">delete&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="c1"># Stop and delete all applications&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">delete&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="c1"># Stop and delete application with ID 0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Startup management&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">startup&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">platform&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Detect init system, generate and configure PM2 startup script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">save&lt;/span> &lt;span class="c1"># Save current process list&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">resurrect&lt;/span> &lt;span class="c1"># Restore previously saved processes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">unstartup&lt;/span> &lt;span class="c1"># Disable and remove startup script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">update&lt;/span> &lt;span class="c1"># Save processes, stop and restore processes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">generate&lt;/span> &lt;span class="c1"># Generate a sample JSON configuration file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Deployment&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">deploy&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="n">prod&lt;/span> &lt;span class="n">setup&lt;/span> &lt;span class="c1"># &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">deploy&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="n">prod&lt;/span> &lt;span class="c1"># &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">deploy&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="n">prod&lt;/span> &lt;span class="n">revert&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="c1"># &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Module system&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">module&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="n">generate&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Generate sample module with specified name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">install&lt;/span> &lt;span class="n">pm2&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">logrotate&lt;/span> &lt;span class="c1"># Install module (includes a logging system)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">uninstall&lt;/span> &lt;span class="n">pm2&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">logrotate&lt;/span> &lt;span class="c1"># Uninstall module&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">publish&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="process-management">
&lt;a class="heading-anchor-link" href="#process-management">Process Management&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="process-management"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Once applications are started, you can easily list and manage them&lt;/p>
&lt;p>List all running processes
&lt;code>$ pm2 list&lt;/code>
Directly manage your processes&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">$ pm2 stop &amp;lt;app_name|id|&amp;#39;all&amp;#39;|json_conf&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ pm2 restart &amp;lt;app_name|id|&amp;#39;all&amp;#39;|json_conf&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ pm2 delete &amp;lt;app_name|id|&amp;#39;all&amp;#39;|json_conf&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="load-balancing-and-zero-downtime-restart">
&lt;a class="heading-anchor-link" href="#load-balancing-and-zero-downtime-restart">Load Balancing and Zero-Downtime Restart&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="load-balancing-and-zero-downtime-restart"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>When starting an application with the &lt;code>-i &amp;lt;instance_option&amp;gt;&lt;/code> option, cluster mode is enabled.
In cluster mode, applications automatically balance requests, and this mode allows you to flexibly improve performance based on the number of CPU cores.
Supported by all major Node.js frameworks and any Node.js application without requiring any code changes.&lt;/p>
&lt;p>Main commands&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">js&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="nb">max&lt;/span> &lt;span class="c1"># Enable load-balancer and start &amp;#39;max&amp;#39; instances (cpu nb)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">reload&lt;/span> &lt;span class="n">all&lt;/span> &lt;span class="c1"># Zero second downtime reload&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">scale&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">app_name&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">instance_number&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="c1"># Increase / Decrease process number&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://keymetrics.io/2015/03/26/pm2-clustering-made-easy/" target="_blank" rel="noopener">More information about using clustering with PM2&lt;/a>&lt;/p>
&lt;h3 id="logging-capabilities">
&lt;a class="heading-anchor-link" href="#logging-capabilities">Logging Capabilities&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="logging-capabilities"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Display logs for specific processes or all processes, real-time, standard, raw, JSON and formatted output&lt;/p>
&lt;p>&lt;code>$ pm2 logs ['all'|app_name|app_id] [--json] [--format] [--raw]&lt;/code>&lt;/p>
&lt;p>Examples:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">logs&lt;/span> &lt;span class="n">APP&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">NAME&lt;/span> &lt;span class="c1"># Display APP-NAME logs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">logs&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="c1"># JSON output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">logs&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">format&lt;/span> &lt;span class="c1"># Formatted output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">flush&lt;/span> &lt;span class="c1"># Flush all logs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">pm2&lt;/span> &lt;span class="n">reloadLogs&lt;/span> &lt;span class="c1"># Reload all logs&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="startup-script-generation">
&lt;a class="heading-anchor-link" href="#startup-script-generation">Startup Script Generation&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="startup-script-generation"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>PM2 can generate and configure startup scripts so that PM2 and your processes can remain running after each server restart.
Supported init systems include: systemd (Ubuntu 16, CentOS, Arch), upstart (Ubuntu 14/12), launchd (macOS, Darwin), rc.d (FreeBSD).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># Auto detect init system + generate and setup PM2 boot at server startup
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ pm2 startup
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Manually specify the startup system
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Can be: systemd, upstart, launchd, rcd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ pm2 startup [platform]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Disable and remove PM2 boot at server startup
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ pm2 unstartup
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Save process list at restart
&lt;code>$ pm2 save&lt;/code>
&lt;a href="http://pm2.keymetrics.io/docs/usage/startup/" target="_blank" rel="noopener">More about startup scripts&lt;/a>&lt;/p>
&lt;h2 id="module-system">
&lt;a class="heading-anchor-link" href="#module-system">Module System&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="module-system"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>PM2 includes a simple yet powerful module system, making it straightforward to install modules.
&lt;code>$ pm2 install &amp;lt;module_name&amp;gt;&lt;/code>
Here are some compatible modules (standalone Node.js applications managed by PM2):&lt;/p>
&lt;p>pm2-logrotate auto rotate logs of PM2 and applications managed
pm2-webshell expose a fully capable terminal in browsers
pm2-server-monit monitor your server health&lt;/p>
&lt;p>&lt;a href="http://pm2.keymetrics.io/docs/advanced/pm2-module-system/" target="_blank" rel="noopener">Write your own module&lt;/a>&lt;/p>
&lt;h3 id="keymetrics-monitoring">
&lt;a class="heading-anchor-link" href="#keymetrics-monitoring">Keymetrics Monitoring&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="keymetrics-monitoring"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If you use PM2 to manage your Node.js apps, Keymetrics makes it easy to monitor applications through the server.
Thank you, hope you enjoy PM2!&lt;/p>
&lt;h2 id="more-resources-about-pm2">
&lt;a class="heading-anchor-link" href="#more-resources-about-pm2">More Resources About PM2&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="more-resources-about-pm2"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/usage/application-declaration/" target="_blank" rel="noopener">Application Declaration via JS files&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/usage/watch-and-restart/" target="_blank" rel="noopener">Watch &amp;amp; Restart&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/usage/pm2-api/" target="_blank" rel="noopener">PM2 API&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/usage/deployment/" target="_blank" rel="noopener">Deployment workflow&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/usage/use-pm2-with-cloud-providers/" target="_blank" rel="noopener">PM2 on Heroku/Azure/App Engine&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/usage/auto-completion/" target="_blank" rel="noopener">PM2 auto completion&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://pm2.keymetrics.io/docs/tutorials/use-pm2-with-aws-elastic-beanstalk/" target="_blank" rel="noopener">Using PM2 in ElasticBeanStalk&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://futurestud.io/tutorials/pm2-utility-overview-installation" target="_blank" rel="noopener">PM2 Tutorial Series&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="changelog">
&lt;a class="heading-anchor-link" href="#changelog">Changelog&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="changelog"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="https://github.com/Unitech/PM2/blob/master/CHANGELOG.md" target="_blank" rel="noopener">CHANGELOG&lt;/a>&lt;/p>
&lt;h2 id="contributors">
&lt;a class="heading-anchor-link" href="#contributors">Contributors&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="contributors"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>&lt;a href="http://pm2.keymetrics.io/hall-of-fame/" target="_blank" rel="noopener">Contributors&lt;/a>&lt;/p>
&lt;h2 id="frequently-asked-questions">
&lt;a class="heading-anchor-link" href="#frequently-asked-questions">Frequently Asked Questions&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="frequently-asked-questions"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>The following issues are not clearly documented in the official documentation but are commonly encountered in practical use. Here are some explanations.&lt;/p>
&lt;h3 id="why-does-pm2-list-sometimes-not-display-appid-and-other-information">
&lt;a class="heading-anchor-link" href="#why-does-pm2-list-sometimes-not-display-appid-and-other-information">Why does &lt;code>pm2 list&lt;/code> sometimes not display appid and other information?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="why-does-pm2-list-sometimes-not-display-appid-and-other-information"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>This mainly depends on the terminal window size, as shown in the image below:&lt;/p>
&lt;p>&lt;figure class="image-figure">
&lt;img
src="https://static.1991421.cn/blog/2017-08-23-145913.jpg"
alt="pm2 list"
loading="lazy"
decoding="async"
class="rounded-lg"
/>
&lt;/figure>&lt;/p>
&lt;p>You&amp;rsquo;ll notice that two executions display different information in the window. The actual reason is that during the first execution, my terminal window was very narrow. After enlarging it and executing the command again, it displays the information shown at the bottom. So to display complete application information, you need to ensure the terminal window is large enough.&lt;/p>
&lt;h3 id="how-to-restart-stop-and-manage-multiple-applications">
&lt;a class="heading-anchor-link" href="#how-to-restart-stop-and-manage-multiple-applications">How to restart, stop, and manage multiple applications?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="how-to-restart-stop-and-manage-multiple-applications"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>For example, if we have applications with appid 0 and 1, besides operating on individual applications or all applications, PM2 actually supports multiple applications. The operation method is as follows:
&lt;code>pm2 start 0 1&lt;/code> - this will start applications with IDs 0 and 1. The same applies to stop, restart, and other operations.&lt;/p>
&lt;h3 id="are-pm2-application-logs-necessary">
&lt;a class="heading-anchor-link" href="#are-pm2-application-logs-necessary">Are PM2 application logs necessary?&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="are-pm2-application-logs-necessary"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Generally, applications managed by PM2 have their own logging systems. For example, when developing Node.js applications, I typically use &lt;code>log4js&lt;/code>, so PM2&amp;rsquo;s logging output is unnecessary and should be disabled.&lt;/p>
&lt;h3 id="environment-variables-undefined-after-pm2-startup">
&lt;a class="heading-anchor-link" href="#environment-variables-undefined-after-pm2-startup">Environment variables undefined after PM2 startup&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="environment-variables-undefined-after-pm2-startup"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Recently, there was a need to call environment variables in web applications. It was discovered that when Node starts normally, environment variables are obtained correctly, but when started with PM2, they are reported as undefined.&lt;/p>
&lt;p>Solution: add the parameter &lt;code>--env production&lt;/code>&lt;/p>
&lt;h2 id="final-thoughts">
&lt;a class="heading-anchor-link" href="#final-thoughts">Final Thoughts&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="final-thoughts"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;p>PM2 is an essential tool for any Node.js developer working in production environments. Its ability to manage processes, provide zero-downtime deployments, and handle load balancing makes it invaluable for maintaining robust applications. The key benefits include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Process management&lt;/strong>: Easy start, stop, restart, and monitoring of Node.js applications&lt;/li>
&lt;li>&lt;strong>Cluster mode&lt;/strong>: Built-in load balancing across multiple CPU cores&lt;/li>
&lt;li>&lt;strong>Zero-downtime deployments&lt;/strong>: Seamless application updates without service interruption&lt;/li>
&lt;li>&lt;strong>Log management&lt;/strong>: Comprehensive logging capabilities with various output formats&lt;/li>
&lt;li>&lt;strong>Startup scripts&lt;/strong>: Automatic configuration to keep applications running after server reboots&lt;/li>
&lt;li>&lt;strong>Module ecosystem&lt;/strong>: Extensible through various community modules&lt;/li>
&lt;/ul>
&lt;p>While PM2 provides excellent logging capabilities, it&amp;rsquo;s worth considering whether you need them if your application already has a robust logging system like log4js. The environment variable issue is a common gotcha, but easily resolved with the &lt;code>--env&lt;/code> parameter.&lt;/p>
&lt;p>Overall, PM2 significantly simplifies Node.js application deployment and management in production environments, making it a must-have tool for serious Node.js development.&lt;/p></description></item><item><title>Nginx Installation and Configuration</title><link>https://en.1991421.cn/2016/09/10/37016/</link><pubDate>Sat, 10 Sep 2016 09:14:03 +0800</pubDate><guid>https://en.1991421.cn/2016/09/10/37016/</guid><description>&lt;blockquote>
&lt;p>Recently deploying web sites, used nginx, here recording main related operations and configurations.
To summarize, nginx is a high-performance http and reverse proxy server. For specific nginx understanding, see &lt;a href="https://nginx.org/en/" target="_blank" rel="noopener">official website&lt;/a> and &lt;a href="https://zh.wikipedia.org/zh-hans/Nginx" target="_blank" rel="noopener">Wikipedia&lt;/a>, learn about it.&lt;/p>
&lt;/blockquote>
&lt;h2 id="related-commands">
&lt;a class="heading-anchor-link" href="#related-commands">Related Commands&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-commands"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Install nginx via yum&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">yum&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="n">install&lt;/span> &lt;span class="n">nginx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Start nginx service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">nginx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Reload nginx configuration&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">nginx&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">s&lt;/span> &lt;span class="n">reload&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Stop nginx service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">$&lt;/span> &lt;span class="n">nginx&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">s&lt;/span> &lt;span class="n">stop&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-configurations">
&lt;a class="heading-anchor-link" href="#related-configurations">Related Configurations&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-configurations"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;h3 id="gzip">
&lt;a class="heading-anchor-link" href="#gzip">gzip&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="gzip"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>After enabling Gzip, it will compress data output to user browsers, reducing data volume transmitted over network, improving browsing speed.&lt;/p>
&lt;p>&lt;strong>Below shows basic gzip configuration information in nginx.conf file&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Gzip&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Enable gzip&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip on&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Minimum file size to enable gzip compression, files smaller than set value won&amp;#39;t compress&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_min_length 1k&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># gzip compression level, 1-10, higher number compresses better but uses more CPU time, detailed explanation later&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_comp_level 2&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># File types to compress. javascript has multiple forms. Values can be found in mime.types file.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_types text/plain application/javascript application/x-javascript text/css text/html application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Whether to add Vary: Accept-Encoding in http header, recommended to enable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_vary on&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Disable IE 6 gzip&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gzip_disable &lt;span class="s2">&amp;#34;MSIE [1-6]\.&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For frontend static resource gzip, we can also adopt pre-compression solution, this way, for user requests, can serve faster, improve performance, like webpack can generate gz files when packaging frontend static resources.
Here shows static gzip configuration item.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">gzip_static on;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Reverse Proxy&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#Define a server, listening on port 80, configured domain is www.1991421.cn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">listen&lt;/span> &lt;span class="mi">80&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># using www domain to access the main website&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">server_name&lt;/span> &lt;span class="n">www&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="mf">1991421.&lt;/span>&lt;span class="n">cn&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_log&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="k">var&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="nb">log&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nginx&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">www&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">log&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">location&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">root&lt;/span> &lt;span class="o">/&lt;/span>&lt;span class="n">usr&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">www&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">website_root&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="http-to-https-forced-redirect">
&lt;a class="heading-anchor-link" href="#http-to-https-forced-redirect">HTTP to HTTPS Forced Redirect&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="http-to-https-forced-redirect"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">server {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> listen 80;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> server_name blog.alanhe.me;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return 301 https://$server_name$request_uri;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">server {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> listen 443 ssl http2 default_server;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> listen [::]:443 ssl http2 default_server;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ssl on;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ssl_certificate &amp;#34;/etc/nginx/ssl/blog.alanhe.me/fullchain.cer&amp;#34;;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ssl_certificate_key &amp;#34;/etc/nginx/ssl/blog.alanhe.me/blog.alanhe.me.key&amp;#34;;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="client_max_body_size">
&lt;a class="heading-anchor-link" href="#client_max_body_size">client_max_body_size&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="client_max_body_size"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>If reporting &lt;code>413 (Request Entity Too Large) &lt;/code> error, need to pay attention to this configuration, default &lt;code>1MB&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>Modify as needed, if don&amp;rsquo;t want restriction, change to 0.&lt;/li>
&lt;li>client_max_body_size can be placed under server or location.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">location / {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> client_max_body_size 0;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="usrsharenginxhtml">
&lt;a class="heading-anchor-link" href="#usrsharenginxhtml">/usr/share/nginx/html&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="usrsharenginxhtml"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;p>Recommended hosting location for static resources placed here.&lt;/p>
&lt;h3 id="referrer-whitelist">
&lt;a class="heading-anchor-link" href="#referrer-whitelist">referrer Whitelist&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="referrer-whitelist"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl"> &lt;span class="k">valid_referers&lt;/span> &lt;span class="s">none&lt;/span> &lt;span class="s">*.1991421.cn&lt;/span> &lt;span class="mi">1991421&lt;/span>&lt;span class="s">.cn&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s">(&lt;/span>&lt;span class="nv">$invalid_referer&lt;/span>&lt;span class="s">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">return&lt;/span> &lt;span class="mi">403&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="related-articles">
&lt;a class="heading-anchor-link" href="#related-articles">Related Articles&lt;/a>
&lt;button
class="heading-anchor"
type="button"
data-anchor="related-articles"
aria-label="Copy anchor link"
title="Copy anchor link"
>
&lt;span class="heading-anchor-wrap" aria-hidden="true">
&lt;svg class="heading-anchor-icon heading-anchor-icon-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" fill="currentColor">
&lt;path d="M0 256C0 167.6 71.6 96 160 96h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C98.1 144 48 194.1 48 256s50.1 112 112 112h72c13.3 0 24 10.7 24 24s-10.7 24-24 24H160C71.6 416 0 344.4 0 256zm576 0c0 88.4-71.6 160-160 160H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c61.9 0 112-50.1 112-112s-50.1-112-112-112H344c-13.3 0-24-10.7-24-24s10.7-24 24-24h72c88.4 0 160 71.6 160 160zM184 232H392c13.3 0 24 10.7 24 24s-10.7 24-24 24H184c-13.3 0-24-10.7-24-24s10.7-24 24-24z">&lt;/path>
&lt;/svg>
&lt;svg class="heading-anchor-icon heading-anchor-icon-copied" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
&lt;path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm107.1 145.1L230.6 325.6c-6.2 6.2-16.4 6.2-22.6 0l-59-59c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0l47.7 47.7 121.1-121.1c6.2-6.2 16.4-6.2 22.6 0s6.3 16.4.1 22.5z">&lt;/path>
&lt;/svg>
&lt;/span>
&lt;/button>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="http://www.infoq.com/cn/news/2016/11/Nginx-when-replace-Apache" target="_blank" rel="noopener">When Will Nginx Replace Apache?&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>