<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>楷鹏 Blog</title>
        <link>https://wukaipeng.com/blog</link>
        <description>楷鹏 Blog</description>
        <lastBuildDate>Wed, 05 Nov 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <item>
            <title><![CDATA[内部分享：开发利器 React Query]]></title>
            <link>https://wukaipeng.com/blog/2025/11/05/2025/ React Qeury</link>
            <guid>https://wukaipeng.com/blog/2025/11/05/2025/ React Qeury</guid>
            <pubDate>Wed, 05 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[背景]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="背景">背景<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2>
<p><code>@tanstack/react-query</code> （下称 RQ）在 GitHub 上已经累积了 4w+ 的 ⭐，称得上 React 技术栈中必不可少的工具。</p>
<p>它并不像 Zustand 这些通用的状态管理库，而是<strong>针对请求这一场景，做了相关的深度优化和功能定制</strong>。</p>
<p>RQ 在当前项目已经引入将近两个月，但是使用情况并不多，障碍可能来自于 RQ 的学习曲线和心智认知，所以开展本次 RQ 分享，让团队各位更多地了解 RQ 的能力，进而利用好 RQ，提高开发效率。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="rq-和-axios-的关系">RQ 和 Axios 的关系<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#rq-%E5%92%8C-axios-%E7%9A%84%E5%85%B3%E7%B3%BB" class="hash-link" aria-label="RQ 和 Axios 的关系的直接链接" title="RQ 和 Axios 的关系的直接链接">​</a></h2>
<p>目前我们的项目中使用 Axios 作为请求器，而 RQ 并不是用来替代 Axios，而是用来增强 Axios 的。</p>
<p>看一下例子：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 👉 这是用 Axios 去实现的获取站内信未读总数的接口方法</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">Promise</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token builtin">number</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> clientApi</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">URL</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">GET_UNREAD_NOTIFICATION_COUNT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fetchOptions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      experimental_throw_all_errors</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      experimental_no_toast_when_error</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 👉 使用 RQ 通过 hook 封装的方式，去增强未读总信接口</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> useQuery </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tanstack/react-query'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notification'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'unread-count'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 👉 RQ 最终调用我们提供给接口方法</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>所以 RQ 其实是请求框架无关的工具，它可以和 Axios，GraphQL，或者原生 <code>fetch()</code> 方法等结合使用。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="功能一减少样板代码">功能一：减少样板代码<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E5%8A%9F%E8%83%BD%E4%B8%80%E5%87%8F%E5%B0%91%E6%A0%B7%E6%9D%BF%E4%BB%A3%E7%A0%81" class="hash-link" aria-label="功能一：减少样板代码的直接链接" title="功能一：减少样板代码的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="不使用-rq">不使用 RQ<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%B8%8D%E4%BD%BF%E7%94%A8-rq" class="hash-link" aria-label="不使用 RQ的直接链接" title="不使用 RQ的直接链接">​</a></h3>
<p>假设我们不使用 RQ，那么正常调用一个接口就是：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> useState</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> useEffect </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NotificationBadge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 👉 定义一堆状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setIsLoading</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setError</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 👉 手动控制各个状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fetchCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetchCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Loading</span><span class="token operator" style="color:#393A34">...</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Error loading notifications</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Unread</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>如果这个接口在多个地方使用，我们需要封装为一个自定义 hook：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 👉 创建自定义 hook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> useState</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> useEffect </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setIsLoading</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setError</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fetchCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetchCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NotificationBadge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Loading</span><span class="token operator" style="color:#393A34">...</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Error loading notifications</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Unread</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="使用-rq">使用 RQ<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%BD%BF%E7%94%A8-rq" class="hash-link" aria-label="使用 RQ的直接链接" title="使用 RQ的直接链接">​</a></h3>
<p>使用 RQ 之后：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 1. 用 RQ 封装自定义 hook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> useQuery </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tanstack/react-query'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notification'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'unread-count'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 2. 引用该 hook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NotificationBadge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Loading</span><span class="token operator" style="color:#393A34">...</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Error loading notifications</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">Unread</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>RQ 相当于语法糖，简化了自定义 hook 的封装，可以大大减少我们写样板代码。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="功能二减少重复请求">功能二：减少重复请求<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E5%8A%9F%E8%83%BD%E4%BA%8C%E5%87%8F%E5%B0%91%E9%87%8D%E5%A4%8D%E8%AF%B7%E6%B1%82" class="hash-link" aria-label="功能二：减少重复请求的直接链接" title="功能二：减少重复请求的直接链接">​</a></h2>
<p>在我们的通知页中，中间菜单和右侧内容都有标题【点赞】以及未读数【3】，这里的未读数都是通过接口获取：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/11/06-001511-aZvvkL-image-20251106001508753.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="不使用-rq-1">不使用 RQ<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%B8%8D%E4%BD%BF%E7%94%A8-rq-1" class="hash-link" aria-label="不使用 RQ的直接链接" title="不使用 RQ的直接链接">​</a></h3>
<p>那么两个地方引用了相同的数据，没有 RQ 的情况下，处理方式有：</p>
<p><strong>状态提升</strong>。把请求接口提升到他们的共同的父组件中。</p>
<ul>
<li>优点: 简单直接，单一数据源。</li>
<li>缺点: 父组件需要知道子组件的数据需求</li>
</ul>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">NotificationPage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setUnreadCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setIsLoading</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetchUnreadCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fetchUnreadCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setUnreadCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">MiddleMenu</span><span class="token plain"> unreadCount</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">RightContent</span><span class="token plain"> unreadCount</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> onRefresh</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">fetchUnreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p><strong>Context API</strong>：使用 <code>&lt;Context&gt;</code> 组件，跨层级共享数据。</p>
<ul>
<li>优点: 避免 Props 穿透（props drilling），组件解耦。</li>
<li>缺点: 任何 context 值变化会导致所有消费者重新渲染，需要额外的 Provider 包裹</li>
</ul>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">NotificationContext</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">NotificationProvider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> children </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setUnreadCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setIsLoading</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fetchUnreadCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setUnreadCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setIsLoading</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetchUnreadCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">NotificationContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">Provider</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isLoading</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">refetch</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> fetchUnreadCount </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">children</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">NotificationContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">Provider</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">MiddleMenu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isLoading </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">NotificationContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">未读</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">RightContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> refetch </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">NotificationContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">未读</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p><strong>封装 Hook</strong>：把请求封装到 hook 中</p>
<ul>
<li>优点：共享请求状态</li>
<li>缺点：每个组件都会重复请求</li>
</ul>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="使用-rq-1">使用 RQ<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%BD%BF%E7%94%A8-rq-1" class="hash-link" aria-label="使用 RQ的直接链接" title="使用 RQ的直接链接">​</a></h3>
<p>使用 RQ 之后：</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">MiddleMenu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> refresh </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">未读</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">RightContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> unreadCount </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">未读</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p><strong>优点：</strong>
✅ <strong>自动去重</strong>: 两个组件同时挂载只发 1 次请求（<code>queryKey</code> 相同）<br>
<!-- -->✅ <strong>无需 Provider</strong>: 不需要包裹父组件<br>
<!-- -->✅ <strong>组件独立</strong>: 每个组件独立使用，不依赖父组件传递</p>
<blockquote>
<p>这里提到"两个组件同时挂载只会发起 1 次请求"，其原理就是 RQ 会缓存请求的数据。RQ 的很多功能都是基于缓存来操作。</p>
</blockquote>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="功能三重刷新">功能三：重刷新<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E5%8A%9F%E8%83%BD%E4%B8%89%E9%87%8D%E5%88%B7%E6%96%B0" class="hash-link" aria-label="功能三：重刷新的直接链接" title="功能三：重刷新的直接链接">​</a></h2>
<p>还是通知的例子，当用户打开"点赞"页面，在这里查看消息的时候，【未读数】需要动态更新：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/11/06-001654-6ZWEZX-image-20251106001653820.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="不使用-rq-2">不使用 RQ<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%B8%8D%E4%BD%BF%E7%94%A8-rq-2" class="hash-link" aria-label="不使用 RQ的直接链接" title="不使用 RQ的直接链接">​</a></h3>
<p>没有 RQ 的情况下，我们需要对这么多个组件做数据联动，方案还是状态提升、Context，当然，还可以通过事件总线（EventEmitter）的方式：</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 定义一个事件总线通用方法：eventBus.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">EventBus</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">callback</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">off</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">events</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">filter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">cb</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> cb </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> eventBus </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">EventBus</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 对于左侧菜单</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">LeftMenu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">totalUnreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setTotalUnreadCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fetchCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadTotalNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setTotalUnreadCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetchCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ⚠️ 监听刷新事件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">handleRefresh</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetchCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    eventBus</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'notification:read'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handleRefresh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      eventBus</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">off</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'notification:read'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handleRefresh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">通知</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">totalUnreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 对于中间的菜单</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">MiddleMenu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setUnreadCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fetchCount</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">setUnreadCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">fetchCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ⚠️ 监听刷新事件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">handleRefresh</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetchCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    eventBus</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'notification:read'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handleRefresh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      eventBus</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">off</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'notification:read'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handleRefresh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">点赞</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 对于右侧内容</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">RightContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">handleMarkAsRead</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">markNotificationAsRead</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ⚠️ 触发刷新事件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    eventBus</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'notification:read'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">button onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">handleMarkAsRead</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">123</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      标记已读</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="使用-rq-2">使用 RQ<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%BD%BF%E7%94%A8-rq-2" class="hash-link" aria-label="使用 RQ的直接链接" title="使用 RQ的直接链接">​</a></h3>
<p>使用 RQ，利用它的 <code>invalidateQueries()</code> 方法可以实现同样的效果：</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 对于左侧菜单</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">LeftMenu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> unreadTotalCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> refresh </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'totalNotificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getTotalUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">staleTime</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Infinite</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">点赞</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 对于中间的菜单</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">MiddleMenu</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> unreadCount </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">点赞</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 对于右侧内容</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">RightContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> queryClient </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQueryClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> unreadCount </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getUnreadNotificationCount</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> notifications </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notifications'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'print-and-collect'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getNotifications</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'print-and-collect'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> markAsReadMutation </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useMutation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">mutationFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> markNotificationAsRead</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">onSuccess</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// ✅ 自动让所有相关查询失效并重新获取</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">invalidateQueries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'totalNotificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">invalidateQueries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">invalidateQueries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notifications'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">h2</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">点赞</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">未读</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">unreadCount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">h2</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">notifications</span><span class="token operator" style="color:#393A34">?.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">n</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div key</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">content</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">isRead</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">button onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> markAsReadMutation</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">mutate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              标记已读</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p><strong>优势</strong>:
✅ <strong>零配置自动同步</strong>: <code>invalidateQueries</code> 一个方法搞定<br>
<!-- -->✅ <strong>组件完全解耦</strong>: <code>&lt;RightMenu&gt;</code>，<code>&lt;MiddleMenu&gt;</code> 和 <code>&lt;RightContent&gt;</code> 互不依赖<br>
<!-- -->✅ <strong>数据一致性</strong>: 始终从服务器获取最新数据<br>
<!-- -->✅ <strong>无需手动管理</strong>: 不需要回调、Context、事件总线<br>
<!-- -->✅ <strong>乐观更新</strong>: 可以先更新 UI，再同步服务器</p>
<blockquote>
<p>RQ 底层也是使用发布/订阅（pub/sub）设计模式。</p>
</blockquote>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="功��能四乐观更新">功能四：乐观更新<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E5%8A%9F%E8%83%BD%E5%9B%9B%E4%B9%90%E8%A7%82%E6%9B%B4%E6%96%B0" class="hash-link" aria-label="功能四：乐观更新的直接链接" title="功能四：乐观更新的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="什么是乐观更新">什么是乐观更新？<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E4%BB%80%E4%B9%88%E6%98%AF%E4%B9%90%E8%A7%82%E6%9B%B4%E6%96%B0" class="hash-link" aria-label="什么是乐观更新？的直接链接" title="什么是乐观更新？的直接链接">​</a></h3>
<p>乐观更新对应的是悲观更新，悲观更新也就是比较传统的做法——先请求 API，再更新 UI。<br>
<!-- -->而<strong>乐观更新是先更新 UI，再请求 API</strong>。</p>
<table><thead><tr><th>✅ 适合场景</th><th>❌ 不适合场景</th></tr></thead><tbody><tr><td>点赞/收藏（高概率成功）</td><td>支付、转账等关键操作</td></tr><tr><td>标记已读/未读</td><td>复杂的业务逻辑（服务器可能拒绝）</td></tr><tr><td>切换开关状态</td><td>数据结构复杂，难以预测结果</td></tr><tr><td>简单的增删改操作</td><td>需要服务器计算的数据（如库存扣减）</td></tr><tr><td>用户期望即时反馈的操作</td><td></td></tr></tbody></table>
<p>了解了乐观更新，我们来引用它去优化上一节提到的内容，先更新未读数的 UI，再请求 API：</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> markAsReadMutation </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useMutation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mutationFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> markNotificationAsRead</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ✅ 乐观更新：立即更新 UI，不等待服务器响应</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">onMutate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">notificationId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 取消正在进行的查询</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">cancelQueries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 保存之前的值（用于回滚）</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> previousCount </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getQueryData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 立即更新未读数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setQueryData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">old</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> old </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> previousCount </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ❌ 如果失败，回滚</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">onError</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">err</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> variables</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> context</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setQueryData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">previousCount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ✅ 无论成功失败，最终都重新获取确保数据一致</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">onSettled</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queryClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">invalidateQueries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'notificationCount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="devtools">DevTools<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#devtools" class="hash-link" aria-label="DevTools的直接链接" title="DevTools的直接链接">​</a></h2>
<p>RQ 提供了一个 Devtools，可以作为调试使用，可以提升我们的开发效率。</p>
<p>可以在项目本地开发环境下集成 Devtools，在右下角这个浮动按钮：</p>
<p>TODO</p>
<p>对于 Devtools 来说，比较好用的主要有 Actions 和 Data Explorer，可以手动查看相关数据，也可以触发 RQ 的相关方法：</p>
<p>TODO</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="深入学习">深入学习<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0" class="hash-link" aria-label="深入学习的直接链接" title="深入学习的直接链接">​</a></h2>
<ol>
<li>
<p><strong>中文文档</strong><br>
<!-- -->目前官方文档只有英文，目前有挺多的相关中文文档镜像，比如：<br>
<a href="https://tanstack.com.cn/query/latest/docs/framework/react/overview" target="_blank" rel="noopener noreferrer">https://tanstack.com.cn/query/latest/docs/framework/react/overview</a><br>
<!-- -->也可以自行 google "tanstack 中文文档"</p>
</li>
<li>
<p><strong>沉浸式翻译</strong><br>
<!-- -->一款 web 插件，一键把英文翻译成中文，原汁原味，适合 RQ 原文档学习。<br>
<a href="https://chromewebstore.google.com/detail/bpoadfkcbjbfhfodiogcnhhhpibjhbnh?utm_source=item-share-cb" target="_blank" rel="noopener noreferrer">https://chromewebstore.google.com/detail/bpoadfkcbjbfhfodiogcnhhhpibjhbnh?utm_source=item-share-cb</a></p>
</li>
<li>
<p><strong>Zread</strong><br>
<!-- -->如果想进一步了解 RQ 的源码或者功能，可以使用 Zread，它已经索引了 RQ 的 GitHub 仓库，并且提供 AI 问答，目前免费： <a href="https://zread.ai/TanStack/query" target="_blank" rel="noopener noreferrer">https://zread.ai/TanStack/query</a></p>
</li>
</ol>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="扩展服务端组件中如何使用">扩展：服务端组件中如何使用？<a href="https://wukaipeng.com/blog/2025/11/05/2025/%20React%20Qeury#%E6%89%A9%E5%B1%95%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%BB%84%E4%BB%B6%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="扩展：服务端组件中如何使用？的直接链接" title="扩展：服务端组件中如何使用？的直接链接">​</a></h2>
<p>留一个问题</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TypeScript 为什么要增加一个 satisfies？]]></title>
            <link>https://wukaipeng.com/blog/typescript-satisfies</link>
            <guid>https://wukaipeng.com/blog/typescript-satisfies</guid>
            <pubDate>Tue, 09 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[最近，在很多依赖库的类型定义文件中，经常能看到了一个陌生的朋友：satisfies。]]></description>
            <content:encoded><![CDATA[<p>最近，在很多依赖库的类型定义文件中，经常能看到了一个陌生的朋友：<code>satisfies</code>。</p>
<p>相信很多人都和我一样，看完 TypeScript 的相关文档，对这个关键字还是一头浆糊。</p>
<p><code>satisfies</code> 关键字是 TypeScript 4.9 版本引入的，用于<strong>类型断言</strong>。</p>
<p>先看一下连接数据库的例子：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Connection</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">declare</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createConnection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  reconnect</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">boolean</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  poolSize</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Connection</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>这里，我们声明了一个函数 <code>createConnection</code>，它接收四个参数，返回一个 <code>Connection</code> 类型。</p>
<p>接着：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">Config</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  tryReconnect</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">boolean</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token builtin">boolean</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  poolSize</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>我们又声明了一个 <code>Config</code> 类型，它包含了四个属性：<code>host</code>、<code>port</code>、<code>tryReconnect</code> 和 <code>poolSize</code>。</p>
<p>接下来：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"localhost"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">tryReconnect</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>我们声明了一个 <code>config</code> 变量，它包含这三个属性的值：<code>host</code>、<code>port</code>、<code>tryReconnect</code>。</p>
<p>OK，现在我们来调用 <code>createConnection</code> 函数，并传入 <code>config</code> 参数：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> poolSize </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> connection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createConnection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> poolSize</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>问题出现了：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/09/09-201148-sgKZDN-image-20250909201147856.png" alt="port 类型错误" class="img_ev3q"></p>
<p>这里 <code>port</code> 的类型是 <code>string | number</code>，而 <code>createConnection</code> 函数的参数类型是 <code>string</code>，所以会报错。</p>
<p>为了解决类型定义问题，我们需要加上类型断言的逻辑代码：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> poolSize </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> port </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"number"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    port </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> connection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createConnection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> poolSize</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p><code>port</code> 类型正确了，但 <code>tryReconnect</code> 类型错误了：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/09/09-202039-skL0mC-image-20250909202039012.png" alt="tryReconnect 类型错误" class="img_ev3q"></p>
<p>我们一次性将这些类型修复：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> poolSize </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> port </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"number"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    port </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> tryReconnect </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tryReconnect </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tryReconnect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> poolSize </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"undefined"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    poolSize </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> connection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createConnection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> poolSize</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p><code>port</code>、<code>tryReconnect</code>、<code>poolSize</code> 都进行了类型断言，问题解决了。</p>
<p>但是，这样写起来很麻烦，有没有更简单的方法呢？</p>
<p>一种方式是，去掉 <code>config</code> 的类型定义，放飞自我，让它自动被推断：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"localhost"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">tryReconnect</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>这样，我们可以一步到位：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tryReconnect </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> connection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createConnection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tryReconnect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>但这样放飞类型，会引起另外的错误，比如 <code>config</code> 随便添加一个属性：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"localhost"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">tryReconnect</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  pool</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 新增了一个属性</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>这样 TypeScript 是一点都不会报错，但却会埋下隐藏炸弹，在代码上线的时候，可能会抓马，<strong>为什么 <code>poorSize</code> 不生效？</strong></p>
<p>层层排查，最后才发现原来 <code>poolSize</code> 写错成了 <code>pool</code>。</p>
<p>这个时候，<code>satisfies</code>，千呼万唤始出来：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"localhost"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">tryReconnect</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  pool</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> satisfies Config</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/09/09-203123-7CAJns-image-20250909203123363.png" alt="pool 类型错误" class="img_ev3q"></p>
<p>不负众望，TypeScript 终于报错，告诉我们 <code>pool</code> 属性不存在。</p>
<p><code>satisfies</code> 关键字为我们提供了一种两全其美的解决方案：</p>
<ol>
<li><strong>保证类型安全</strong>：它会检查我们的对象结构是否<strong>满足</strong>（satisfies）指定的类型（如 <code>Config</code>）。如果你写了多余的属性（如 <code>pool</code>），或者属性类型不匹配，TypeScript 会立刻报错。这避免了“放飞自我”带来的隐患。</li>
<li><strong>保留原始类型</strong>：与使用类型注解 (<code>: Config</code>) 不同，<code>satisfies</code> <strong>不会改变变量被推断出的原始具体类型</strong>。变量 <code>config</code> 的 <code>port</code> 属性类型仍然是 <code>number</code>，<code>tryReconnect</code> 属性类型仍然是 <code>() =&gt; boolean</code>。</li>
</ol>
<p>总结来说，<code>satisfies</code> 的核心优势在于：<strong>在不丢失（泛化）原始推断类型的前提下，对该值进行类型检查。</strong></p>
<p>这使得我们既能获得编辑器对于具体类型的智能提示和类型推断的好处，又能确保这个值的结构符合我们预先定义好的更宽泛的类型约束，从而写出更安全、更灵活的代码。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="references">REFERENCES<a href="https://wukaipeng.com/blog/typescript-satisfies#references" class="hash-link" aria-label="REFERENCES的直接链接" title="REFERENCES的直接链接">​</a></h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html" target="_blank" rel="noopener noreferrer">TypeScript 4.9</a></li>
<li><a href="https://www.youtube.com/watch?v=6uJeT7y6CCo" target="_blank" rel="noopener noreferrer">Satisfies Operator - new TypeScript 4.9 feature!</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[如何同时打开多个 Chrome 呢？]]></title>
            <link>https://wukaipeng.com/blog/infinite-chrome</link>
            <guid>https://wukaipeng.com/blog/infinite-chrome</guid>
            <pubDate>Thu, 03 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[哈喽，我是楷鹏。]]></description>
            <content:encoded><![CDATA[<p>哈喽，我是楷鹏。</p>
<p>今天想要分享 Chrome 的一个小技巧，可以一次性打开多个干净独立的 Chrome，让你的开发更丝滑。</p>
<p>开头做个小调查，你平时开发的时候，会使用哪些浏览器呢？</p>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Chrome</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Firefox</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->Safari</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->其他</li>
</ul>
<p>我平时开发的时候，主力就是使用 Chrome。</p>
<p>Chrome 的 DevTools 功能非常强大，满足前端开发调试的绝大数需求。</p>
<p>但是长期来有一个困扰我的问题，就是我的日常使用和开发是<strong>耦合</strong>在一起的。</p>
<p>比如，我的 Chrome 会装载很多的插件：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/07/08-213533-tgVkG2-image-20250708213533037.png" alt="Chrome Extensions" class="img_ev3q"></p>
<p>这些插件会影响我的开发，因为他们可能在页面中会插入 HTML 或者 CSS 代码，以及会产生很多额外的请求，干扰我的正常开发调试。</p>
<p>比如下面侧边栏的插件 HTML：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/08/06-000130-dQfKmL-image-20250806000129455.png" alt="Chrome Layer Tab" class="img_ev3q"></p>
<p>此时的选择，要么是开启无痕窗口，要么是换另外一个浏览器。</p>
<p>这两种方式都不错，但无痕窗口还是使用同一个 Chrome 实例，并且重新打开无痕窗口，所有的状态都会被清空。</p>
<p>另外一种方式是换另外一个浏览器，我曾经尝试过，但是后来又放弃了，换一个浏览器就相当于换一种全新的开发环境，需要重新适应界面、操作习惯等等，真的很别扭。</p>
<p>最近学到了另一种新方式，就是可以通过使用不同的用户数据目录，来创建不同的 Chrome 实例。</p>
<p>运行命令：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/Applications/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome.app/Contents/MacOS/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome --user-data-dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"/tmp/chrome_user_dir_1"</span><br></span></code></pre></div></div>
<p>你就可以创建一个全新的 Chrome 实例，并且这个实例的配置、插件、历史记录等都是独立的。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/07/08-002759-3B2OHR-image-20250708002759144.png" alt="Create Chrome Instance" class="img_ev3q"></p>
<p>甚至在 Dock 栏，你还可以看到两个 Chrome 图标：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/07/08-003053-0GuO3E-image-20250708003053037.png" alt="Chrome Instances in Dock" class="img_ev3q"></p>
<p>这个新创建的 Chrome 实例，完全可以看作是一个全新的 Chrome 浏览器。</p>
<p>你可以修改主题，来和其他 Chrome 实例区分开来：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/07/08-210920-t5HzRo-image-20250708210919880.png" alt="Modify Theme" class="img_ev3q"></p>
<p>或者登录不同的账号等等操作，这是完全属于你的第二 Chrome。</p>
<p>通过运行这条命令，理论上你可以创建无限个 Chrome 实例，只需要修改 <code>--user-data-dir</code> 参数即可，比如：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/Applications/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome.app/Contents/MacOS/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome --user-data-dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"/tmp/chrome_user_dir_2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/Applications/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome.app/Contents/MacOS/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome --user-data-dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"/tmp/chrome_user_dir_3"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/Applications/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome.app/Contents/MacOS/Google</span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"> Chrome --user-data-dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"/tmp/chrome_user_dir_4"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">..</span><span class="token punctuation" style="color:#393A34">..</span><span class="token punctuation" style="color:#393A34">..</span><br></span></code></pre></div></div>
<p>不过平时实际使用的时候，我一般使用两个 Chrome 实例，来回切换，一个用于网站浏览，一个用于开发调试。</p>
<p>在开发调试的时候，每次打开项目再打开新的 Chrome 会有一点点烦躁，所以你可以考虑将这条命令写入到你的前端项目 <code>package.json</code> 的脚本中：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"dev"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next dev --turbopack"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"open-chrome"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --args --user-data-dir=/tmp/ChromeNewProfile http://localhost:3000"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"dev:chrome"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"npm run open-chrome &amp;&amp; npm run dev"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><br></span></code></pre></div></div>
<p>这样你就可以通过 <code>npm run dev:chrome</code> 来打开 Chrome 实例，并且自动运行 <code>next dev</code> 命令。</p>
<blockquote>
<p>Windows PowerShell 用户可以使用：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token property" style="color:#36acaa">"dev"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next dev --turbopack"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token property" style="color:#36acaa">"open-chrome"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"powershell -Command \"Start-Process 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' -ArgumentList '--user-data-dir=D:\\temp\\ChromeNewProfile', 'http://localhost:3000'\""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token property" style="color:#36acaa">"dev:chrome"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"npm run open-chrome &amp;&amp; npm run dev"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><br></span></code></pre></div></div>
</blockquote>
<p>如果你希望打开 Chrome 实例的时候，同时打开 <code>localhost:3000</code> 页面来看到页面效果，可以在命令后面直接添加 <code>http://localhost:3000</code>：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"dev"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"next dev"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"dev:chrome"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=\"/tmp/chrome_user_dir_1\" http://localhost:3000 &amp;&amp; npm run dev"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>好了，这就是本期的全部内容，如果对你有帮助，欢迎点赞、收藏、转发。</p>
<p>我是楷鹏，我们下期再见。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Next.js 路由跳转显示进度条：使用 BProgress 提升用户体验]]></title>
            <link>https://wukaipeng.com/blog/nextjs-router-progress</link>
            <guid>https://wukaipeng.com/blog/nextjs-router-progress</guid>
            <pubDate>Mon, 09 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[学习如何在 Next.js 15+ 应用中实现页面跳转进度条，解决路由切换时的用户体验问题。包含完整的 BProgress 集成教程和代码示例。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本期已录制 B 站视频 👉 <a href="https://www.bilibili.com/video/BV1NcNdzZEKx/?share_source=copy_web&amp;vd_source=d2ccb0630a0a92ceb5b5801eeaaad7b7" target="_blank" rel="noopener noreferrer">【Next.js】路由跳转显示进度条</a></p>
</blockquote>
<p>哈喽，我是楷鹏。</p>
<p>先来看一个反面教材。</p>
<p>在 Dify.ai 中，当点击跳转页面之后，会有一段需要等待的时间，然后才会跳转页面。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-001014-b22Y3p-dify.gif" alt="Dify.ai 页面跳转时缺少进度反馈" class="img_ev3q"></p>
<p>然而，中间这段时间我并不知道是否跳转成功了，所以我会多点了几下，直到跳转。</p>
<p>这种体验很不好 👎</p>
<p>解决方案很简单，我们来看一下 GitHub 的跳转交互。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-001027-wIEPR6-github.gif" alt="GitHub 页面跳转时的进度条效果" class="img_ev3q"></p>
<p>可以看到，GitHub 在跳转期间，会显示一个进度条，清晰地告诉用户——"我正在跳转，请稍等"。</p>
<p>那么在 Next.js 中，如何实现这个效果呢？</p>
<p>我们可以借助 <a href="https://bprogress.vercel.app/" target="_blank" rel="noopener noreferrer">BProgress</a> 这个库来实现。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-001139-FNp34B-iShot_2025-06-22_00.11.25.jpg" alt="BProgress 官网首页展示" class="img_ev3q"></p>
<p>BProgress 是一个轻量级的进度条组件库，支持 Next.js 15+，同时也支持 Remix、Vue 等其他框架。</p>
<p>对于 BProgress 的使用，我做了一个 demo 项目 <a href="https://github.com/wukaipeng-dev/nextjs-progress-bar-demo" target="_blank" rel="noopener noreferrer">nextjs-progress-bar-demo</a>，我们可以把这个项目先 clone 下来：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone git@github.com:wukaipeng-dev/nextjs-progress-bar-demo.git</span><br></span></code></pre></div></div>
<p>然后进入项目目录：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">cd</span><span class="token plain"> nextjs-progress-bar-demo</span><br></span></code></pre></div></div>
<p>先安装依赖：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> @bprogress/next</span><br></span></code></pre></div></div>
<p>启动项目：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-001402-BiLNbr-image-20250622001402486.png" alt="Next.js 进度条演示项目界面" class="img_ev3q"></p>
<p>可以看到，这是一个简单的 Next.js 项目，包含三个页面：首页、登录页、注册页。</p>
<p><code>main</code> 分支已经配置好了进度条，我们切换到分支 <code>without-progress-bar-demo</code>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> checkout without-progress-bar-demo</span><br></span></code></pre></div></div>
<p>当前分支下，我们没有配置进度条，所以跳转页面时，不会显示进度条。</p>
<p>接下来我们在根布局 <code>app/layout.tsx</code> 中引入 <code>ProgressProvider</code>：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">'use client'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./globals.css"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">ProgressProvider</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@bprogress/next/app'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RootLayout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  children</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Readonly</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  children</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">ReactNode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">en</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">ProgressProvider</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">4px</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">color</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#4c3aed</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">options</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> showSpinner</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript boolean" style="color:#36acaa">false</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">shallowRouting</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">children</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">ProgressProvider</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>接下来，我们可以看一下，在首页和登录页、登录页和注册页之间跳转，都会显示一个进度条。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-001736-Lqx6xz-basic-integrate.gif" alt="集成 BProgress 后的页面跳转效果" class="img_ev3q"></p>
<p><code>ProgressProvider</code> 的参数如下：</p>
<ul>
<li><code>height</code>：进度条的高度</li>
<li><code>color</code>：进度条的颜色</li>
<li><code>options</code>：进度条的配置，这里 <code>showSpinner</code> 设置为 <code>false</code>，表示不显示一个动画的加载图标。</li>
<li><code>shallowRouting</code>：是否启用浅层路由，如果开启的话，只改变路由的 query 参数，比如 <code>?page=1</code> 变成 <code>?page=2</code>，那么进度条不会重新加载。</li>
</ul>
<p>但是，当我们登录成功之后，再点击跳转，却不会显示进度条。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-001924-LDl4LE-no-progress-when-login.gif" alt="使用 router.push 跳转时进度条未显示" class="img_ev3q"></p>
<p>这是因为，首页和登录页、登录页和注册页之间，是使用 <code>&lt;Link&gt;</code> 组件进行跳转的。</p>
<p><code>&lt;Link&gt;</code> 组件实际会渲染成 <code>&lt;a&gt;</code>，BProgress 通过给所有 <code>&lt;a&gt;</code> 组件添加点击事件，来显示进度条。</p>
<p>我们可以看下在 DevTools → Elements → <code>&lt;a&gt;</code> → Event Listeners 中，是否添加了点击事件：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-002255-d9dYdk-iShot_2025-06-22_00.22.04.webp" alt="DevTools 中查看 Link 组件的点击事件监听器" class="img_ev3q"></p>
<p>但是，当我们登录成功之后，则是使用 <code>router.push</code> 进行跳转的。</p>
<p>BProgress 不会给 <code>router.push</code> 添加点击事件，自然也不会显示进度条。</p>
<p>不用慌，BProgress 为我们提供了 <code>useRouter</code> 方法。</p>
<p>将 Next.js 的 <code>useRouter</code> 替换为 BProgress 提供的 <code>useRouter</code>：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// import { useRouter } from 'next/navigation';</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRouter </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@bprogress/next/app'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>然后，正常使用即可：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> router </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRouter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">router</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>这时，你可以看到，在登录成功之后，自动跳转首页时，进度条就能正常显示了。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/06/22-002512-FZVedD-show-progress-when-login.gif" alt="使用 BProgress 的 useRouter 后进度条正常显示" class="img_ev3q"></p>
<p>但如果你的项目已经封装过了自己的 <code>useRouter</code>，那么你可以将封装过的 <code>useRouter</code> 作为参数 <code>customRouter</code> 传入，进行二次封装：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRouter </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@bprogress/next/app'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useRouter </span><span class="token imports keyword module" style="color:#00009f">as</span><span class="token imports"> useNextIntlRouter </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@/i18n/navigation'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Home</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> router </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRouter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    customRouter</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> useNextIntlRouter</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">        router</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript method function property-access" style="color:#d73a49">push</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript string" style="color:#e3116c">'/about'</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">          startPosition</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">0.3</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">          locale</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript string" style="color:#e3116c">'en'</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">        </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">      </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      Go to about page</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>最后，让我们回到 <code>app/layout.tsx</code>，这里我们引入了 <code>ProgressProvider</code>，但却把 <code>app/layout</code> 变成了一个客户端组件，我们来把 <code>ProgressProvider</code> 抽离到其他地方，仍然保持 <code>app/layout</code> 是一个服务端组件。</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// app/components/ProgressWrapper.tsx</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">'use client'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">ProgressProvider</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@bprogress/next/app'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">ProgressWrapperProps</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  children</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">ReactNode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ProgressWrapper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> children </span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">ProgressWrapperProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">ProgressProvider</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">4px</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">color</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">#0000ff</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">options</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> showSpinner</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript boolean" style="color:#36acaa">false</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">shallowRouting</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">children</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">ProgressProvider</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>在 <code>app/layout.tsx</code> 中，我们引入 <code>ProgressWrapper</code>：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">ProgressWrapper</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./components/ProgressWrapper'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RootLayout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  children</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Readonly</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  children</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">ReactNode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">en</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">ProgressWrapper</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">children</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">ProgressWrapper</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>好的，不愧是你，完成了一个 Next.js 集成路由跳转显式进度条的封装。</p>
<p>以上就是本期的全部内容，希望对你有所帮助。</p>
<p>感谢观看！👏</p>]]></content:encoded>
            <category>Next.js</category>
            <category>React</category>
            <category>前端开发</category>
            <category>用户体验</category>
            <category>性能优化</category>
            <category>教程</category>
        </item>
        <item>
            <title><![CDATA[谁分得清 Next.js、Nest.js、Nuxt.js 啊]]></title>
            <link>https://wukaipeng.com/blog/next-nest-nuxt</link>
            <guid>https://wukaipeng.com/blog/next-nest-nuxt</guid>
            <pubDate>Sat, 31 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[作为一个前端 er，工作或者学习中，至少会遇到这么一次，需要区分 Next.js、Nest.js、Nuxt.js 的场景。]]></description>
            <content:encoded><![CDATA[<p>作为一个前端 er，工作或者学习中，至少会遇到这么一次，需要区分 <strong>Next.js</strong>、<strong>Nest.js</strong>、<strong>Nuxt.js</strong> 的场景。</p>
<p>我最近就遇到这么一次。</p>
<p>公司有一位新入职的同事，吃饭聊天的时候，听他说之前做过 <strong>Next.js</strong> 的项目。</p>
<p>由于公司最近的新项目基于 Next.js，我就在想，“太好了，我们的新项目有救了”。</p>
<p>结果，在群聊的时候，他澄清了下，做的是 <strong>Nest.js</strong> 的项目。</p>
<p>一下子给我立不住了。</p>
<p>作为一个说着普通话的普通程序员，听不清 Next /nekst/ 和 Nest /nest/ 这两个发音，实在是太正常了。</p>
<p>这些框架的起名作者真是聪明。</p>
<p>命名跟批发一样，都是 N__t.js，上一次让我这么犯难的，还是黄金届的“周大福、周六福、周生生、六福珠宝……”</p>
<p>而这场<strong>品牌碰瓷</strong>，其实主要集中在前端框架爆发的 2016 年左右。</p>
<p>那个时候，前端行业百花齐放，各种框架层出不穷。</p>
<p>在 2016 年 10 月 25 号，Next.js 1.0 发布，首次作为开源项目亮相。</p>
<p>Next.js 基于 React 框架，提供服务端渲染（SSR）和静态站点生成（SSG）功能，以及自动代码拆分、路由系统等特性。</p>
<p>随后的一天，也就是 10 月 26 号，Nuxt.js 发布。</p>
<p>不得不说，Nuxt.js 抄得真快，它基于 Vue.js，整出了另一个 Next.js 翻版。</p>
<p>而 Nest.js 则是在下一年 2017 年的 2 月 26 号发布，它跟 Next.js 和 Nuxt.js 关系就比较远了。</p>
<p>它是纯 Node.js 后端框架，属于是 JavaScript 届的 Spring Boot。</p>
<p>现在这三个框架都发展得很好，除了打铁自身硬之外，是不是更多地得益于“<strong>蹭热度</strong>”的命名呢？</p>
<p>或许下一次，开发新框架的时候，就叫做 <strong>Not.js</strong> 吧。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[最强 Node.js 版本管理器，比 NVM 还好用]]></title>
            <link>https://wukaipeng.com/blog/volta</link>
            <guid>https://wukaipeng.com/blog/volta</guid>
            <pubDate>Sat, 24 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Volta 介绍]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="volta-介绍">Volta 介绍<a href="https://wukaipeng.com/blog/volta#volta-%E4%BB%8B%E7%BB%8D" class="hash-link" aria-label="Volta 介绍的直接链接" title="Volta 介绍的直接链接">​</a></h2>
<p>最开始在安装 Node.js 的时候，我们只能通过官网下载安装包，安装指定的一个版本，比如 18.17.1。</p>
<p>但对于不同的项目，我们可能需要使用不同的 Node.js 版本，比如 18.17.1 和 20.17.1。</p>
<p>如果要去切换版本，那就需要卸载旧版本，安装新版本，再切换项目，非常麻烦（痛苦面具）。</p>
<p>于是出现了 Node.js 版本管理器，比如 <a href="https://github.com/nvm-sh/nvm" target="_blank" rel="noopener noreferrer">NVM</a>、<a href="https://volta.sh/" target="_blank" rel="noopener noreferrer">Volta</a> 等。</p>
<p>它支持安装指定版本的 Node.js，并且可以自由切换版本。</p>
<p>但是，NVM 存在一些问题，for example，无法根据项目自动切换版本，不支持 Windows 平台（当然，有一个非官方支持的野鸡 <a href="https://github.com/coreybutler/nvm-windows" target="_blank" rel="noopener noreferrer">nvm-windows</a> 可以使用） 等等。</p>
<p>新一代的 Node.js 版本管理器 <a href="https://volta.sh/" target="_blank" rel="noopener noreferrer">Volta</a> 解决了这些问题。</p>
<p>它可以根据项目自动切换 Node.js 版本，并且还支持 Mac、Windows、Linux 三大平台。</p>
<p>Volta 基于 Rust 开发，速度更快，活更好。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="安装-volta">安装 Volta<a href="https://wukaipeng.com/blog/volta#%E5%AE%89%E8%A3%85-volta" class="hash-link" aria-label="安装 Volta的直接链接" title="安装 Volta的直接链接">​</a></h2>
<p>根据<a href="https://docs.volta.sh/guide/getting-started" target="_blank" rel="noopener noreferrer">安装指南</a>，我们在终端中输入以下命令来安装 Volta：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-fsSL</span><span class="token plain"> https://get.volta.sh </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">bash</span><br></span></code></pre></div></div>
<p>安装完成后，打开另一个新终端，输入以下命令来查看当前的 Volta 版本：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">volta </span><span class="token parameter variable" style="color:#36acaa">-v</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2.0</span><span class="token plain">.2</span><br></span></code></pre></div></div>
<p>恭喜你，Volta 安装成功。</p>
<p>接下来，我们就可以使用 Volta 来管理 Node.js 版本了。</p>
<p>在终端中输入以下命令来安装 Node.js：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">volta </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">node</span><br></span></code></pre></div></div>
<p>这条命令会安装最新 LTS 版本的 Node.js。</p>
<blockquote>
<p>LTS：Long Term Support，长期支持版本。</p>
</blockquote>
<p>当然，也可以用艾特符号 <code>@</code> 安装特定版本的 Node.js，比如：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">volta </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> node@20.17.1</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="项目级-nodejs-版本管理">项目级 Node.js 版本管理<a href="https://wukaipeng.com/blog/volta#%E9%A1%B9%E7%9B%AE%E7%BA%A7-nodejs-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86" class="hash-link" aria-label="项目级 Node.js 版本管理的直接链接" title="项目级 Node.js 版本管理的直接链接">​</a></h2>
<p>打开一个你正在维护的 Node.js 项目，比如“shit-mountain”，找到 <code>package.json</code> 文件，添加以下内容：</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">//...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"volta"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"node"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"20.17.1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>当你执行 <code>npm i</code> 时，Volta 会寻找 20.17.1 版本的 Node.js。</p>
<p>如果找不到，Volta 会自动安装 20.17.1 版本的 Node.js，然后再执行 <code>npm i</code>。</p>
<p>这样就确保了项目中使用的 Node.js 版本为 20.17.1。</p>
<p>Volta 还有其他一些特性，比如 Volta 的各种命令，<code>list</code>、<code>uninstall</code> 等等，又比如 Hooks，可以指定下载源，这里就不再展开。</p>
<p>前往 Volta 官网查看更多信息 👉 <a href="https://volta.sh/" target="_blank" rel="noopener noreferrer">https://volta.sh</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[开源项目如何提高吸引力？试试添加 Vercel 一键部署]]></title>
            <link>https://wukaipeng.com/blog/vercel</link>
            <guid>https://wukaipeng.com/blog/vercel</guid>
            <pubDate>Sun, 19 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[在一些成名的 GitHub 开源项目中，会支持 Vercel 一键部署，比如前两年爆火，如今坐拥 78.7k star 的 NextChat：]]></description>
            <content:encoded><![CDATA[<p>在一些成名的 GitHub 开源项目中，会支持 Vercel 一键部署，比如前两年爆火，如今坐拥 78.7k star 的 <a href="https://github.com/ChatGPTNextWeb/NextChat?tab=readme-ov-file" target="_blank" rel="noopener noreferrer">NextChat</a>：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283120518-mLqxXh-3044a98f77c84a7191deec7fbe2f1b1d.png" alt="" class="img_ev3q"></p>
<p>那么 Vercel 是什么呢？ 它是一个专为前端开发者设计的<strong>现代化部署平台</strong>，特别适合用于<strong>静态网站和前端应用</strong>的构建、预览和发布。</p>
<p>So，如果你的开源项目属于静态网站或者前端应用一类的，可以考虑 README.md 上<strong>添加 Vercel 一键部署，为你的开源项目增加吸引力</strong>。</p>
<p>添加一键部署的方式也很简单，Vercel 提供了一个<strong>按钮生成工具</strong>：<a href="https://vercel.com/docs/deployments/deploy-button" target="_blank" rel="noopener noreferrer">deploy-button</a></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283120723-5ppjrX-702a4ec30d1c4f96b8ab8fb7f4e1e208.png" alt="" class="img_ev3q"></p>
<p>按钮生成器会生成 Markdown、HTML 和 URL 三种方式，可以按需取用</p>
<blockquote>
<p>提醒一下，这里的交互会有点奇怪，页面下方是表单输入，比如填写 Git 仓库地址之后，上方 Markdown 链接会自动改变，并且没有成功提醒，这里需要适应一下</p>
</blockquote>
<p>这里必填的，只有你的 <strong>Git 仓库地址</strong>：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283120951-cla5No-ea7f8ae650564f7489a2c7316fde2133.png" alt="" class="img_ev3q"></p>
<p>其他的还有像是环境变量、默认项目名称、重定向、Demo、集成等，<strong>按需填写</strong>，最后将生成好的 Markdown 贴到你的开源项目 README.md 上：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283121127-iqyZZM-a3d1c64c282249e2a403c03a62a8b5e0.png" alt="" class="img_ev3q"></p>
<p><strong>整个流程就完成了</strong>，非常简单</p>
<p>对于用户侧来说，当他点击部署按钮之后，就会跳转到 Vercel 网站：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283121487-w1u0qY-9af3c8b78217432ba5cd715a94ea89a4.png" alt="" class="img_ev3q"></p>
<p>这里需要登录 Vercel，同时 Vercel 会要求授予 Git 仓库读写权限，因为 Vercel 会执行<strong>对目标仓库的克隆</strong>，再以克隆后的仓库为准进行部署：</p>
<p>填写项目名称，点击创建：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283122589-LCgi3S-18fe758e918c4f3d9c7f7a112e9420bc.gif" alt="" class="img_ev3q"></p>
<p>接下来就是等待大功告成：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283123130-l91ujh-ac96dfa0cac34b5eacea153877655aa0.png" alt="" class="img_ev3q"></p>
<p><strong>Congratulations！</strong></p>
<p>这里已经能看到网站运行成功之后的<strong>预览截图</strong>了，也可以点击「Continue to Dashboard」去到控制台，点击 domain 网址，同样能看到网站已经成功部署：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2025/01/19-1737283124104-lbtiOe-2de354a20b8840b688a4a4b43aa758de.gif" alt="" class="img_ev3q"></p>
<p>整体回顾来看，Vercel 的部署服务非常丝滑，我甚至都不需要提供框架信息、运行命令等等</p>
<p>So，觉得 Vercel  一键部署的方式不错，那么考虑为你的项目增加一下吧！</p>
<blockquote>
<p>如有需要，可查看本文示例项目参考：<a href="https://github.com/wukaipeng-dev/ken" target="_blank" rel="noopener noreferrer">https://github.com/wukaipeng-dev/ken</a></p>
</blockquote>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[重度使用一年，我是如何把滴答清单用到极致？]]></title>
            <link>https://wukaipeng.com/blog/ticktick</link>
            <guid>https://wukaipeng.com/blog/ticktick</guid>
            <pubDate>Fri, 03 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Raycast 的 2024 年度统计出来了，这份统计包含了我这一年来用 Raycast 去打开其他应用的情况，看到滴答清单被打开上千次，排列榜首，还是有点小意外]]></description>
            <content:encoded><![CDATA[<p>Raycast 的 2024 年度统计出来了，这份统计包含了我这一年来用 Raycast 去打开其他应用的情况，看到滴答清单被打开上千次，排列榜首，还是有点小意外</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012103-21Jwww-a198c020c5cc431487d87a5e9be925da.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>滴答清单英文名：TickTick</p>
</blockquote>
<p>今年是尝试并逐渐重度使用滴答清单的一年，一开始，我只想要找一个任务管理的软件，对比过其他很多 todo 软件，滴答清单的免费版本是我个人认为最良心的，大部分功能都涵盖到了</p>
<p>虽然免费很诱人，但还不足以打动我，真正打动的点还是解决了我长久以来的痛点。</p>
<p>在做任务的时候，我用的是番茄工作法，这种方法指的是<strong>在 25 分钟（1 个番茄钟）内专注，然后 5 分钟短休息，累计 4 个番茄钟后进行长休息</strong>这样的一个周期性节奏去专注</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012103-MVPruL-a825b85f8da34d26a6b28a0cefc84bf2.png" alt="" class="img_ev3q"></p>
<p>之前我买了一个桌面计时钟，考研党朋友应该很熟悉，类似于下面这种：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012103-60J4J2-614474f4bf4d47ce970fe3d4e5a41a01.png" alt="" class="img_ev3q"></p>
<p>这种计时钟既可以正计时，也可以倒计时，也就是做一个 25 分钟的番茄钟倒计时，就这我用了 2 年多，在家里买一个，在公司也买了一个。</p>
<p>它很方便，物理按键，一按就可以开启番茄钟，但它的优点也正是它的缺点。</p>
<p>物理的限制，我不可能随时随地带着一个计时钟，更麻烦的是，一天结束，我不知道每天花了多少番茄钟，不知道什么时候开启番茄钟，不知道番茄钟期间都做了什么任务。</p>
<p>滴答清单在添加任务之后，可以针对这个任务直接开启番茄钟，也可以在番茄钟上去关联对应的任务，同时能做一些额外的笔记，打开统计页面，能清晰地看到每个番茄钟的开始结束</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012103-jeA8Bq-d3029f1122d54837accae40fb7ed9cf3.png" alt="" class="img_ev3q"></p>
<p>这基本上解决了我的大痛点，另外一个小痒点就是它的通知功能，日常总会忘记开启番茄钟，滴答清单在电脑的菜单栏，可以显式地看到专注的进度：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-C5e9Ho-dabd490b317849558cc8b81fab433758.png" alt="" class="img_ev3q"></p>
<p>如果开启了跨设备同步，那么手机和电脑都会实时同步当前的专注进度：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-jGOOxJ-1d6a47d0fdc041fcad73adf10533c0b3.png" alt="" class="img_ev3q"></p>
<p>让我小小惊喜的是，移动端开启专注模式后，会自动进入沉浸模式，其中除了翻页时钟模式，还有下面这款像素模式，UI 做得特别好看：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-1Gw383-4f1ba5a5272245f9be23f0b066e53eee.png" alt="" class="img_ev3q"></p>
<p>滴答清单在解决我的 Big Trouble 之后，我开始慢慢上手其他功能。</p>
<p>对于核心功能——任务管理，第一个提的点是对任务的分类，我相信很多人的分类都比较杂乱，今天加一个「读书」的类目，明天加一个「项目管理」的类目，而且类目一旦多了起来就难以管理了</p>
<p>这里我推荐一个非常实用的分类方式，那就是<strong>按照「角色」划分</strong></p>
<p>在公司里，角色就是「员工」，在学校里，角色就是「学生」，在跑道上，角色就是「运动员」，<strong>在任意的社会关系中，每个人都是不同的角色</strong>。</p>
<p>在我个人的滴答任务分类中，会有管理员（Admin）、员工（Worker）、开发者（Dev）、Friend（朋友）、读书人（Reader）等等角色</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-WHGC9A-d6f45fafa35f4a59a2d9ef6b48391097.png" alt="" class="img_ev3q"></p>
<p>这种划分方法非常稳定，我的任务分类一旦划分后，几乎没有大的改动，它还足够灵活，比如喜当爹了，那就加一个「父亲」角色，买奶粉、换尿布的任务都怼在这个角色上</p>
<p>按照角色划分，几乎囊括了所有的事情，但对于某些角色来说，它需要横向扩展，比如作为员工，需要做项目 a、项目 b 、项目 c 等等，那么可以使用标签，为任务打上对应标签，也就能够把角色下不同的任务类型区分开来：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-AtwuWE-2658643c5770471888720e48bd94e54d.png" alt="" class="img_ev3q"></p>
<p>通过角色划分 + 标签系统，基本可以建立一个有序稳定的分类体系了</p>
<p>第二点是任务处理方面，滴答清单藏了很多小心思，比如可以设置预估任务使用的番茄钟数量：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-eNOfae-82056f8929c44fa58dedfcf63623e7f2.png" alt="" class="img_ev3q"></p>
<p>还比如可以设置任务进度百分比：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-plT2n4-ef8dcaa2c9a648788ca761fdb77211c1.gif" alt="" class="img_ev3q"></p>
<p>按住 Shift 或者 Command/Control 键选中多个任务之后，能够进行批量处理：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012104-wK5ELq-c20594b673354d5fb989e0904827b374.png" alt="" class="img_ev3q"></p>
<p>移动端长按应用图标添加任务，任务框右下角有个语音转文字功能，可以加速添加任务时间</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012105-6mYm9u-523e7d76cbbf4c79a1955511f5035110.png" alt="" class="img_ev3q"></p>
<p>另外是日程表功能，之前没有相应的使用习惯，最近发现了两点，让我开始觉得日程表非常香。</p>
<p>第一点发现是，可以运用筛选面板去查看目标任务，之前没有使用筛选，看着日历上所有任务都堆在一起，一个头两个大：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012105-qZMuxy-e892015725d14094bdba7863ad29b7bd.png" alt="" class="img_ev3q"></p>
<p>现在使用筛选功能，按照清单、标签等筛选，可以轻松地查看日程对应的任务：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012105-c7GTwR-66f3bee45055469d92393413e392dcea.png" alt="" class="img_ev3q"></p>
<p>第二点发现是原来日程之间是有通用协议的——<a href="https://wukaipeng.com/weekly/9#7-calcav" target="_blank" rel="noopener noreferrer">CalDAV</a>，它是一种日历数据共享和同步的协议，适用于安卓、iPhone、Windowns、macOS 等一切设备，只需要日历源即可在需要的日历地方导入即可同步到日程。</p>
<p>我在滴答清单上通过导入飞书的 CalCAV 配置，即能实现对飞书所有会议、日程的订阅：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012105-Br8Jel-f7c076cdea59469f942a50c135e2b770.png" alt="" class="img_ev3q"></p>
<p>还有一个习惯功能，我挖掘出了三种使用方法：</p>
<p>第一种就是最常用的正习惯：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012105-3f0itB-d472ba35970e4299bc68d7f89056b7d7.png" alt="" class="img_ev3q"></p>
<p>第二种是坏习惯：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012105-cjpwo2-45f856050b2d434ea42fddc07dddf927.png" alt="" class="img_ev3q">
这种和正习惯相反，只有在出现这些坏习惯的时候才会做记录，坏习惯记录的场景是有：</p>
<ol>
<li>记录一些低频、偶发的坏习惯</li>
<li>当培养成功出为每日习惯后，不需要再频繁记录，只需要记录某天未做的异常</li>
</ol>
<p>第三是数据记录</p>
<p>习惯是有自带日历，可以当做一个数据记录，比如减肥的过程，可以记录每天的体重情况：
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012106-mLaqvx-ee237d0fe0bf48cfbb9f0ba37ab1f785.png" alt="" class="img_ev3q"></p>
<p>以上就是个人对滴答清单这一年来的使用，这确实是一款优秀的软件，但不可否认，它也存在一些局限性，比如艾森豪威尔矩阵，仅仅是对任务进行重要性、紧急性做一个简单的二维划分：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012106-hvK80x-e17de740c7674b5a89c09ed2ee434e19.png" alt="" class="img_ev3q"></p>
<p>但这世界上不可能有完美的软件，有问题就解决问题，2024 年陆陆续续给滴答清单提的 bug 加上 feature request，有将近 30 个：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2025/01/03-012106-JP97Rb-0533529051ee418aa18a4a0664c6cd85.png" alt="" class="img_ev3q"></p>
<p>所以，我这算是编外的测试人员 + 产品经理吗 😆</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Mac 备忘录妙用]]></title>
            <link>https://wukaipeng.com/blog/mac-notes</link>
            <guid>https://wukaipeng.com/blog/mac-notes</guid>
            <pubDate>Sun, 13 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[之前使用 Windows 的过程中，最痛苦的事是没有一款可以满足我快速进行记录的应用]]></description>
            <content:encoded><![CDATA[<p>之前使用 Windows 的过程中，最痛苦的事是没有一款可以满足我<strong>快速进行记录</strong>的应用</p>
<p>基本都得先打开该笔记软件，然后创建新笔记，最后才能输入，这么多步骤太麻烦了</p>
<p>在切换到 MacOS 之后，让我惊喜的就是自带的备忘录，只需要<strong>简单地把鼠标移动到屏幕右下角</strong>，就可以创建一篇快速备忘录</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232208-p1Fxnh-3f8e5b02ad934b37b03659c0452af268.gif" alt="" class="img_ev3q"></p>
<p>Amazing！</p>
<p>这种方式叫做<strong>触发角</strong>，触发角可以在「系统设置 » 桌面与程序坞 » 触发角」设置：
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232208-O6KaVV-1b54c6ffecfe4d7fa91477391c82dd6a.png" alt="" class="img_ev3q">
四个触发角分别可以自由设置：
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232209-EJ0gJE-d6e28a4f9f134588b02a15f5306ba679.png" alt="" class="img_ev3q"></p>
<p>除了触发角，快捷键【 fn(🌐） + Q】同样能创建一篇快速备忘录</p>
<p>还有一个问题是，触发角 or 快捷键默认会打开上一次编辑的备忘录，如果想要<strong>每次都创建一篇新的快速备忘录</strong>的话，可以在设置这里：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232209-ywHvRu-57fd2a786a00457986e8b2c68d784f7f.png" alt="" class="img_ev3q">
把「始终回到上个快速备忘录」取消勾选</p>
<p>备忘录支持<strong>大部分高频的文本样式</strong>，选取文本后，在头部导航栏 Aa 这里做修改样式：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232209-dG8cIX-e8dfb79f7e4b43808e13150c484db1a4.png" alt="" class="img_ev3q">
也能支持 check 清单：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232209-djMwgQ-0497afc39df441a29dfea46c8fc712fa.png" alt="" class="img_ev3q">
表格功能比较弱鸡，<strong>就一个简单的表格</strong>，什么合并、冻结等高级功能都没有</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232209-ojOhO8-4d8327cb2b06489a839cf3b07b4805b3.png" alt="" class="img_ev3q"></p>
<p>另外还有图片、链接，这里就不再赘述。</p>
<p>备忘录默认支持文件夹分类，另外还<strong>支持标签分类</strong>，只需要在备忘录中使用井号（<code>#</code>）加上对应文字，Mac 即会生成对应的标签清单：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-i2Paen-f7f5af6816f14b6c97768bc4415f224e.png" alt="" class="img_ev3q"></p>
<p>之前在浏览网页的时候，特别想<strong>高亮某些内容，同时做一些拓展记录</strong>，安装过插件 Weava Highlighter，但是不好用，每次只要选中文字就 Weava 就会弹出，特别烦人。</p>
<p>没想到 Mac 备忘录居然原生支持这个功能</p>
<p>在 Safari 中，可以选择想要收藏的内容，右键「添加到快速备忘录」</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-4UsMyC-5b9541a4a8b94c2fad0e1a4c01230a88.png" alt="" class="img_ev3q">
创建快速备忘录之后，选中的这句话在 Safari 中会被高亮：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-6umB4e-688c3d6114b743a5b72c2df1fb0bd54d.png" alt="" class="img_ev3q"></p>
<p>在最新的 MacOS 15 中更新中，备忘录新支持了<strong>录音功能</strong>：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-EuzAws-e868ae71a6454655b7bf2afeeb3a3a0f.png" alt="" class="img_ev3q"></p>
<p>并且还支持<strong>实时的语言转文本</strong>，但目前又又又又<strong>仅支持英语</strong></p>
<p>库克的母语是英语，我的母语是无语 😅</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-7CyHWr-02148d8fbd15401c9dc5a7380edfc68c.gif" alt="" class="img_ev3q"></p>
<p>另外，还新增了<strong>高亮颜色</strong>，分别有紫色、粉色、橙色、薄荷色和蓝色，不得不说，这几种颜色确实还挺好看的</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-KejmGl-24abe463c0ce4b8891ddf687028cfb48.png" alt="" class="img_ev3q"></p>
<p>最有用的功能当属于这个<strong>数学功能</strong>了</p>
<p>直接输入像是 <code>(27/3)^2=</code> 或者 <code>47*96=</code> 算式，备忘录会自动计算结果：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232210-TFwKhn-9d357ffa366f499a804a6ccfed0452f0.gif" alt="" class="img_ev3q"></p>
<p>还支持自定义变量：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/10/13-232211-fH3sw7-8969a94bba62451dae880010baad67f2.gif" alt="" class="img_ev3q"></p>
<p>总体来说，Mac 的备忘录还算是一个不错的笔记软件，虽然缺乏像 Notion 的文档目录结构和块编辑的一些先进笔记能力，但它有着原生的支持，<strong>能够满足快速记录和基础编辑的需求</strong></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[前端开发实用的面试备考分享]]></title>
            <link>https://wukaipeng.com/blog/interview</link>
            <guid>https://wukaipeng.com/blog/interview</guid>
            <pubDate>Sat, 21 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[最近一句话很火「好的工作就像 HIV，只通过母婴，血液和性传播」]]></description>
            <content:encoded><![CDATA[<p>最近一句话很火「<strong>好的工作就像 HIV，只通过母婴，血液和性传播</strong>」</p>
<p>排除 HIV 患者，对于普通家庭的程序员朋友来说，面试是必经之路</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-122530-HWdvRQ-0XJ0X.gif" alt="" class="img_ev3q"></p>
<p>一般来说，面试会有这么四个困难：</p>
<ol>
<li>技术太菜：基础不牢固？算法不熟练？</li>
<li>项目不足：没有亮眼的项目？都是失败项目？</li>
<li>话少嘴笨：不会表达？说话没有逻辑？</li>
<li>心态问题：简历石投大海着急？一面挂了自闭？裸辞焦虑？</li>
</ol>
<p>这些其实都是表症，真正的病根是：</p>
<p><strong>对面试没有系统性的认知和准备</strong></p>
<p>接下来我将从几方面给大家分享一下个人的面试备考思路</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="一对自己要有清晰的认知">一、对自己要有清晰的认知<a href="https://wukaipeng.com/blog/interview#%E4%B8%80%E5%AF%B9%E8%87%AA%E5%B7%B1%E8%A6%81%E6%9C%89%E6%B8%85%E6%99%B0%E7%9A%84%E8%AE%A4%E7%9F%A5" class="hash-link" aria-label="一、对自己要有清晰的认知的直接链接" title="一、对自己要有清晰的认知的直接链接">​</a></h2>
<p>很多人<strong>心比天高</strong>，但往往<strong>命比纸薄</strong></p>
<p>对自己没有清晰的认知和规划，进入就业市场一开始野心勃勃，但很快就被现实来几个大逼兜</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-122247-vJ1Ycq-7XMnj.jpeg" alt="" class="img_ev3q"></p>
<p>面试一开始，要<strong>厘清自己的现有资源</strong>，比如对于我：</p>
<ul>
<li>学历方面，双非本科</li>
<li>技术方面，基础一般，算法不会</li>
<li>工作方面，没有入职过大厂</li>
<li>项目方面，没有中大型的项目经验</li>
</ul>
<p>这些都是我在面试中的劣势，那我的优势是什么：</p>
<ul>
<li>年轻白菜价，三年工作经验，有一定的项目经验，对企业来说性价比高</li>
<li>有些许写作成就，同时维护一个个人的技术博客网站</li>
<li>心态 open，愿意学习，愿意尝试</li>
</ul>
<p>综合分析自己的现有资源，我得出的结论：</p>
<ul>
<li>有一定市场竞争力，但还<strong>达不到大厂的门槛</strong>，可以冲一冲中厂</li>
<li>次一点的结果是小厂或者创业公司</li>
<li>最差的就是大厂的子公司、外包公司或者华为 OD</li>
</ul>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="二做好准备打一个持久战">二、做好准备，打一个持久战<a href="https://wukaipeng.com/blog/interview#%E4%BA%8C%E5%81%9A%E5%A5%BD%E5%87%86%E5%A4%87%E6%89%93%E4%B8%80%E4%B8%AA%E6%8C%81%E4%B9%85%E6%88%98" class="hash-link" aria-label="二、做好准备，打一个持久战的直接链接" title="二、做好准备，打一个持久战的直接链接">​</a></h2>
<p>面试就是打仗，而且是一个持久战</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="钱">钱<a href="https://wukaipeng.com/blog/interview#%E9%92%B1" class="hash-link" aria-label="钱的直接链接" title="钱的直接链接">​</a></h3>
<p>首先就是「钱」这方面，尤其裸辞，是否有足够的储蓄支撑自己的生活</p>
<p>不要想着一两个月就能找到满意的工作，这是不现实的</p>
<p>尤其是现有大环境的情况下，<strong>至少要规划半年以上的时间</strong>的储蓄</p>
<p>该消费降级的还是要降级，该省的还是要省</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="心态">心态<a href="https://wukaipeng.com/blog/interview#%E5%BF%83%E6%80%81" class="hash-link" aria-label="心态的直接链接" title="心态的直接链接">​</a></h3>
<p>其次是「心态」，大部分人刚开始找工作自信满满</p>
<p>但随着时间推移，投简历没有回复，面试没有通过，家人朋友的关心和询问也会让你感到压力</p>
<p>这时候很容易产生焦虑，甚至自闭，我也有过这样的经历</p>
<p>我的做法是「记录」下来，<strong>接纳自己的情绪，给自己积极的心理暗示</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-120927-TfOVag-1726891755359.png" alt="" class="img_ev3q"></p>
<p>罗翔老师曾经说过：对于不可控的事情，我们要保持乐观；对于可控的事情，我们要保持谨慎</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="体力">体力<a href="https://wukaipeng.com/blog/interview#%E4%BD%93%E5%8A%9B" class="hash-link" aria-label="体力的直接链接" title="体力的直接链接">​</a></h3>
<p>最后是「体力」，既然找工作是一个持久战，那就需要有足够的体力支撑</p>
<p>一直待在家里刷着求职网站，海投无果、不断被拒，在这种精神和体力的双重内耗之下，人很容易崩溃</p>
<p>不如出去走走，呼吸新鲜空气，做一些运动</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-090918-yWP83B-4mBWE.gif" alt="" class="img_ev3q"></p>
<p><strong>保持一个健康的身心，反而能保持自己的自信心和提升求职效率</strong></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="三工具提效">三、工具提效<a href="https://wukaipeng.com/blog/interview#%E4%B8%89%E5%B7%A5%E5%85%B7%E6%8F%90%E6%95%88" class="hash-link" aria-label="三、工具提效的直接链接" title="三、工具提效的直接链接">​</a></h2>
<p>这一次备考，主要使用了这么一些工具</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="flomo">Flomo<a href="https://wukaipeng.com/blog/interview#flomo" class="hash-link" aria-label="Flomo的直接链接" title="Flomo的直接链接">​</a></h3>
<p>Flomo 是一个「简单、高效」的<strong>碎片笔记工具</strong>，可以帮助你记录、整理、回顾自己的备考过程</p>
<p>我在 Flomo 中主要记录了自己的面试经历、面试题目、面试感受、面试反思等等</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-123545-ceocwI-image-20240921123545224.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="飞书">飞书<a href="https://wukaipeng.com/blog/interview#%E9%A3%9E%E4%B9%A6" class="hash-link" aria-label="飞书的直接链接" title="飞书的直接链接">​</a></h3>
<p>飞书支持<strong>飞书个人版</strong>，这次备考主要是使用了「飞书文档」 和「飞书妙记」</p>
<p>首先是飞书文档，真的很香，文档类型丰富，有像 Notion 一样的块文档，还有思维导图、Base 二维表格、画板等等</p>
<p>我用飞书文档做一个 <a href="https://v0oyefxd4a.feishu.cn/wiki/BmYzwLm1jidxJ8khdBicTgCpnlh" target="_blank" rel="noopener noreferrer">✨ 前端求职大攻略</a> ，下一节会提到</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-125930-dllW6p-image-20240921125929824.png" alt="" class="img_ev3q"></p>
<p>飞书妙记的话，可以上传语音 MP3，然后转成文字，这个功能主要用于<strong>面试后的自我复盘</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-130321-19B07V-image-20240921130321182.png" alt="" class="img_ev3q"></p>
<p>你可以点击点击文字，直接跳转到对应的音频位置，非常方便，还有倍速播放、跳过空白等功能</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-130547-LJfCvs-image-20240921130547616.png" alt="" class="img_ev3q"></p>
<p>如果你还没用过飞书 👉 <a href="https://events.feishu.cn/7371707527425196033/recommend-form?i_code=9eI%2FDUQtwNN48zKqEo6BESIxiketv3hf&amp;reward_type=cash" target="_blank" rel="noopener noreferrer">邀请使用飞书</a></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="滴答清单">滴答清单<a href="https://wukaipeng.com/blog/interview#%E6%BB%B4%E7%AD%94%E6%B8%85%E5%8D%95" class="hash-link" aria-label="滴答清单的直接链接" title="滴答清单的直接链接">​</a></h3>
<p>滴答清单是一个「<strong>任务管理工具</strong>」，主要用来做备考计划</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-135638-8m3wbq-image-20240921135638493.png" alt="" class="img_ev3q"></p>
<p>另外滴答清单有一个不错的番茄钟专注功能，用来跟进每天的任务开销和状态</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-140157-wdmILx-image-20240921140059308.png" alt="" class="img_ev3q"></p>
<p>如果你还没用过滴答清单 👉 <a href="https://dida365.com/wechatInvite?c%3D3%26p%3D55y8ia7e%26t%3D0=" target="_blank" rel="noopener noreferrer">邀请使用滴答清单</a></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ai-类工具">AI 类工具<a href="https://wukaipeng.com/blog/interview#ai-%E7%B1%BB%E5%B7%A5%E5%85%B7" class="hash-link" aria-label="AI 类工具的直接链接" title="AI 类工具的直接链接">​</a></h3>
<p>不得不承认，现在 AI 工具的效果真的不错</p>
<p>我本来想要付费找一些前辈做一些备考指导，但是使用 AI 工具也能达到不错的效果，而且免费</p>
<ul>
<li><strong>ChatGPT、Claude</strong>：回答质量不错，麻烦的是需要梯子 🪜</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-140556-ruzcB9-image-20240921140555813.png" alt="" class="img_ev3q"></p>
<ul>
<li><strong>豆包</strong>：字节跳动家的 AI 问答产品，对于非技术类的问题，回答质量赶得上 ChatGPT，并且支持 PC 客户端，我非常喜欢的一个功能是像 Spotlight 一样，<strong>快捷键唤起</strong>就可以向直接 AI 提问</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-141009-83Wew3-image-20240921141009221.png" alt="" class="img_ev3q"></p>
<ul>
<li><strong>腾讯元宝</strong>：回答效果一般，但胜在可以搜索相关微信公众号文章再做回答，毕竟现在中文高质量回答都被圈在微信封闭的平台上</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-141217-yt1QBd-image-20240921141217154.png" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="四做一个个人的求职攻略">四、做一个个人的求职攻略<a href="https://wukaipeng.com/blog/interview#%E5%9B%9B%E5%81%9A%E4%B8%80%E4%B8%AA%E4%B8%AA%E4%BA%BA%E7%9A%84%E6%B1%82%E8%81%8C%E6%94%BB%E7%95%A5" class="hash-link" aria-label="四、做一个个人的求职攻略的直接链接" title="四、做一个个人的求职攻略的直接链接">​</a></h2>
<p>我认为，每个求职的程序员都应该做一个个人的求职攻略</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="为什么做一个求职攻略">为什么做一个求职攻略<a href="https://wukaipeng.com/blog/interview#%E4%B8%BA%E4%BB%80%E4%B9%88%E5%81%9A%E4%B8%80%E4%B8%AA%E6%B1%82%E8%81%8C%E6%94%BB%E7%95%A5" class="hash-link" aria-label="为什么做一个求职攻略的直接链接" title="为什么做一个求职攻略的直接链接">​</a></h3>
<p>其实原因很简单，就是把备考的过程系统化，将备考的攻略、求职记录、面试题目、面试过程等都<strong>汇总在一个知识库</strong>中</p>
<p>抛开这一次面试，你的求职攻略可以用到下一次面试，现在的付出也是在<strong>增援未来的自己</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/21-235633-47XUXi-8xpXZ.jpeg" alt="" class="img_ev3q"></p>
<p>个人选用飞书文档也是刚好它比较强大，思维导图也能免费使用</p>
<p>当然，用自己熟练的笔记应用也可以，比如语雀、Notion、Obsidian，甚至 Word、Excel 也可以</p>
<p>软件工具只是手段，重要的是你的<strong>思考和记录</strong></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="求职攻略参考内容">求职攻略参考内容<a href="https://wukaipeng.com/blog/interview#%E6%B1%82%E8%81%8C%E6%94%BB%E7%95%A5%E5%8F%82%E8%80%83%E5%86%85%E5%AE%B9" class="hash-link" aria-label="求职攻略参考内容的直接链接" title="求职攻略参考内容的直接链接">​</a></h3>
<p>目前个人的飞书文档 <a href="https://v0oyefxd4a.feishu.cn/wiki/BmYzwLm1jidxJ8khdBicTgCpnlh" target="_blank" rel="noopener noreferrer">✨ 前端求职大攻略</a> 已经公开，欢迎大家参考</p>
<p>首先是《大攻略》部分，按照求职时间线排列，涵盖求职的各种事项</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-003207-rRIEzc-image-20240922003207872.png" alt="" class="img_ev3q"></p>
<p>如果觉得烦，可以只看《Lite 极简说明书》</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-003351-4rxhs4-image-20240922003351583.png" alt="" class="img_ev3q"></p>
<p>接下来就是《作业 1：PDCJ》这次面试对于<strong>阶段性复盘</strong>我使用了 PDCJ ，它是一种思维模型：</p>
<ul>
<li>P 是 Plan 计划</li>
<li>D 是 Do 做事</li>
<li>C 是 Check 检查</li>
<li>J 是 Just 调整。</li>
</ul>
<p>通过阶段性地计划、施行，再不断地检查、调整，确保自己的面试<strong>正确又高效</strong>地前行，不会出轨。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-003549-0Y7SDN-image-20240922003549038.png" alt="" class="img_ev3q"></p>
<p>《作业 2：面试题》这个比较重要，除了技术八股文之外，还要深入挖掘自己的项目</p>
<p>不仅仅是成功的项目，失败的项目也要挖掘，让面试官看到到你<strong>对项目的深层次、多角度的思考</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-004025-jJtXw5-image-20240922004025124.png" alt="" class="img_ev3q"></p>
<p>然后就是《作业3：个性化自我介绍》，这个就是 show 自己</p>
<p>在面试中，<strong>自我介绍是最重要的环节之一</strong>，它可以让面试官对你有一个初步的印象</p>
<blockquote>
<p>不过也看面试官，有的面试官可能不太喜欢开头的「自我介绍」，他们更喜欢直接问问题，比如我遇到上来就直接问“介绍一下你做过的最有成就感的项目”</p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-081224-ICCDLF-image-20240922081224352.png" alt="" class="img_ev3q"></p>
<p>对于《作业 4：公司跟进表》，维护自己所有的投递状态，包括公司名称、岗位、投递时间、面试状态等等</p>
<p>目前程序员求职渠道还是挺多的，用这个表可以维护自己的求职状态，能够一个<strong>统筹和及时跟进</strong></p>
<p>当这个数据量持续增长之后，你也可以<strong>分析出自己的投递效率</strong>，比如你会发现内推的简历通过率会比 Boss 直聘投递高很多，这样你就可以调整自己的求职策略</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-091910-77Mu4E-image-20240922091910140.png" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="五最后一些小建议">五、最后一些小建议<a href="https://wukaipeng.com/blog/interview#%E4%BA%94%E6%9C%80%E5%90%8E%E4%B8%80%E4%BA%9B%E5%B0%8F%E5%BB%BA%E8%AE%AE" class="hash-link" aria-label="五、最后一些小建议的直接链接" title="五、最后一些小建议的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="1-不要否定自己">1. 不要否定自己<a href="https://wukaipeng.com/blog/interview#1-%E4%B8%8D%E8%A6%81%E5%90%A6%E5%AE%9A%E8%87%AA%E5%B7%B1" class="hash-link" aria-label="1. 不要否定自己的直接链接" title="1. 不要否定自己的直接链接">​</a></h3>
<p>考试会考砸，谈恋爱会分手，面试会挂，这些都是正常的</p>
<p><strong>人生失败才是常态，成功是少数</strong></p>
<p>不要因为面试失败就着急否定自己</p>
<p>如果失败了，就去复盘，去反思，去总结，再去尝试（PDCJ）</p>
<p>分享一句很喜欢的话：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-005535-rsZSuY-1.webp" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="2-谦虚">2. 谦虚<a href="https://wukaipeng.com/blog/interview#2-%E8%B0%A6%E8%99%9A" class="hash-link" aria-label="2. 谦虚的直接链接" title="2. 谦虚的直接链接">​</a></h3>
<p>叔本华说过，<strong>每个人都把自己视野的极限，当做世界的极限</strong>。</p>
<p>井底之蛙的对世界极限认知是一口井</p>
<p>我之前就犯过这样的错误，以为自己很厉害，但是越往深处学，越发现自己的渺小和无知</p>
<p>现在面试的时候，就保持谦逊的态度</p>
<p>面试遇到自己的强势领域，娓娓道来，展示你的能力和思考</p>
<p>遇到自己的弱势领域，坦诚承认，表示愿意学习和提升</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="3-关于外包公司">3. 关于外包公司<a href="https://wukaipeng.com/blog/interview#3-%E5%85%B3%E4%BA%8E%E5%A4%96%E5%8C%85%E5%85%AC%E5%8F%B8" class="hash-link" aria-label="3. 关于外包公司的直接链接" title="3. 关于外包公司的直接链接">​</a></h3>
<p>外包公司的话，<strong>能不去就不去</strong></p>
<p>待遇这方面，工资、年终奖等先被外包公司扒一层皮</p>
<p>工作内容方面，往往是一些维护、二次开发、测试等杂活</p>
<p>技术方面，外包公司的技术氛围往往不好，身边外包同事可能都比较躺平</p>
<p>自尊心方面，外包的同学往往是低人一等，公司内网权限、工牌、餐卡等都在提示你和正式员工的差距</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-102922-5Rg8AW-7a00b3fe7f86bd5f7710d974cd2ca02e.jpeg" alt="" class="img_ev3q"></p>
<p>所以，除非是过渡阶段（1-2 年），外包公司不建议去</p>
<p>另外，<strong>不建议用外包岗位来作为面试练手</strong></p>
<p>外包岗位的面试题目、难度、面试官心态等和正常岗位有区别，参考意义不大</p>
<p>并且还要投入相当多的时间和精力，比如和 HR 对接、约笔试、面试、通勤去现场面试等</p>
<p>没必要做这种无用功，还是保存体力，专注正常岗位的面试</p>
<p>对于 Boss 直聘，可以在设置 &gt; 隐私保护 &gt; 屏蔽公司 中，把外包公司都屏蔽掉，避免干扰</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com//2024/09/22-103515-BW9eXE-img_v3_02ev_da4b7316-beea-456a-b534-4c4593ed9a1g.jpg" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="4-利用你能利用的一切">4. 利用你能利用的一切<a href="https://wukaipeng.com/blog/interview#4-%E5%88%A9%E7%94%A8%E4%BD%A0%E8%83%BD%E5%88%A9%E7%94%A8%E7%9A%84%E4%B8%80%E5%88%87" class="hash-link" aria-label="4. 利用你能利用的一切的直接链接" title="4. 利用你能利用的一切的直接链接">​</a></h3>
<p>之前听过，创业的状态是「<strong>小姐心态，寡妇待遇，妇联追求</strong>」</p>
<p>求职的过程，这句话也适用</p>
<p>你的追求和目的是寻求一份满意的工作，那就放下身段，动用你能利用的一切资源</p>
<p>把你的朋友圈、同学圈、前同事挨个联系一遍，问问有没有内推的机会</p>
<p>发邮箱找大佬，看看有没有合适的工作岗位 or 请教面试的相关事宜</p>
<p>加技术群，加 QQ 群，加微信群，多和大佬交流，多和同行交流</p>
<p>把自己的求职面扩大，不要局限在 Boss 直聘、拉勾、猎聘等求职网站</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 要离职了，记录一下个人在用的 Mac 应用]]></title>
            <link>https://wukaipeng.com/blog/mac-application</link>
            <guid>https://wukaipeng.com/blog/mac-application</guid>
            <pubDate>Wed, 26 Jun 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[通用]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="通用">通用<a href="https://wukaipeng.com/blog/mac-application#%E9%80%9A%E7%94%A8" class="hash-link" aria-label="通用的直接链接" title="通用的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="飞书">飞书<a href="https://wukaipeng.com/blog/mac-application#%E9%A3%9E%E4%B9%A6" class="hash-link" aria-label="飞书的直接链接" title="飞书的直接链接">​</a></h3>
<p>说起来不信，第一个推荐的是【飞书】，飞书是目前用过最舒服的项目管理应用了。</p>
<p>单拎出来一个<strong>飞书文档</strong>，功能和体验<strong>远超</strong>市面上腾讯文档、石墨文档、语雀等等。</p>
<p>现在飞书还支持<strong>个人版</strong>，No more thumbs up。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210639-8TvEm9-4627432cf1b94af3ac0ffa95d7a9a401.png" alt="" class="img_ev3q"></p>
<p>除了文档，飞书还支持邮箱、视频通话等功能，飞书 = 企业微信 + 腾讯文档 + QQ 邮箱 + 腾讯会议，直接 <strong>All in One</strong> 了</p>
<p>飞书还自带一些贴心的小功能，比如 <strong>OCR（图片转文字）、录视频、录 GIF</strong>，以及一个非常使用的「<strong>滚动截图</strong>」，截长图不烦恼</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="arc">Arc<a href="https://wukaipeng.com/blog/mac-application#arc" class="hash-link" aria-label="Arc的直接链接" title="Arc的直接链接">​</a></h3>
<p>现在主力浏览器已经从 Chrome 切换到【Arc】，Arc 的<strong>多空间</strong>解决了我的一直以来的痛点需求。</p>
<p>并且会自动收起过期的 Tab 页面，<strong>支持全屏，颜值好看，交互舒服</strong>，Arc 值得拥有。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210639-RGRaBZ-d4d0cfb965e740018694efa7ed1b6ce6.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="有道翻译">有道翻译<a href="https://wukaipeng.com/blog/mac-application#%E6%9C%89%E9%81%93%E7%BF%BB%E8%AF%91" class="hash-link" aria-label="有道翻译的直接链接" title="有道翻译的直接链接">​</a></h3>
<p>程序员看官方技术文档，<strong>翻译是刚需</strong>。</p>
<p>本人也算是有道翻译的老用户了，陪着从有道词典改名为现在的【有道翻译】。</p>
<p>虽然有道翻译的翻译效果不是最好的，但胜在<strong>词典内容丰富，划词交互方便</strong>。</p>
<p>目前加上了 AI，翻译质量也上了一个台阶，怒冲两年会员。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210640-LjSWJi-aca5f1fa03b04fa5a8f6200ea64b3338.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="grammarly-desktop">Grammarly Desktop<a href="https://wukaipeng.com/blog/mac-application#grammarly-desktop" class="hash-link" aria-label="Grammarly Desktop的直接链接" title="Grammarly Desktop的直接链接">​</a></h3>
<p>有时候提 issue 或者写英文的场景下，担心自己语法错误，Grammarly Desktop 派上用场。</p>
<p>基础的<strong>语法检验 free</strong>，但是润色以及 AI 功能需要付费。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210640-giZcRy-a35a25744523409a8f817f51350968fc.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ibar">iBar<a href="https://wukaipeng.com/blog/mac-application#ibar" class="hash-link" aria-label="iBar的直接链接" title="iBar的直接链接">​</a></h3>
<p>手头的 Mac 有刘海屏，应用一多就会被刘海挡住，很烦。</p>
<p>iBar 可以把菜单栏收起一下。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210640-EqfVIb-ffd48ba82c684ac099c9c927d4cb2cb1.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ishot">iShot<a href="https://wukaipeng.com/blog/mac-application#ishot" class="hash-link" aria-label="iShot的直接链接" title="iShot的直接链接">​</a></h3>
<p>截取一张包<strong>含圆角、阴影</strong>的好看截图，可以用 iShot。</p>
<p>Free 版本足够，长截图、OCR、录屏等高级付费功能，可以用飞书自带的。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210640-bwh8DX-4b207245170e4b30b1312d65d041f39c.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="snipaste">Snipaste<a href="https://wukaipeng.com/blog/mac-application#snipaste" class="hash-link" aria-label="Snipaste的直接链接" title="Snipaste的直接链接">​</a></h3>
<p>之前从 Win 转 Mac 的时候，一如既往 Snipaste，它的<strong>贴图功能</strong>很好用</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210641-WOzqt5-0e9c54f649ed45559b226ed41f19ad1f.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="app-cleaner--uninstaller">App Cleaner &amp; Uninstaller<a href="https://wukaipeng.com/blog/mac-application#app-cleaner--uninstaller" class="hash-link" aria-label="App Cleaner &amp; Uninstaller的直接链接" title="App Cleaner &amp; Uninstaller的直接链接">​</a></h3>
<p>App Cleaner &amp; Uninstaller 强力<strong>卸载软件</strong>，干干净净
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210642-mFb9YY-608f905f7e994015985ea8481018d935.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="runcat">Runcat<a href="https://wukaipeng.com/blog/mac-application#runcat" class="hash-link" aria-label="Runcat的直接链接" title="Runcat的直接链接">​</a></h3>
<p>在菜单栏上看<strong>一只猫不断地奔跑</strong>也是一种消遣</p>
<p>Runcat 可以在菜单栏用 Runcat <strong>查看当前 CPU 等信息</strong>，CPU 用得多小猫也就跑得越快</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210642-lBB6Cj-5c9fc83b470642c7ad2eac6f4fb40540.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="activate-mac">Activate Mac<a href="https://wukaipeng.com/blog/mac-application#activate-mac" class="hash-link" aria-label="Activate Mac的直接链接" title="Activate Mac的直接链接">​</a></h3>
<p>一个百无聊赖的小挂件，Win 的「Activate Windows」类似，在屏幕右下角展示「<strong>Activate macOS</strong>」</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210643-sZCRev-df65d35512f54bacaff50c97e76a55b0.png" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="效率">效率<a href="https://wukaipeng.com/blog/mac-application#%E6%95%88%E7%8E%87" class="hash-link" aria-label="效率的直接链接" title="效率的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="个人任务三件套">个人任务三件套<a href="https://wukaipeng.com/blog/mac-application#%E4%B8%AA%E4%BA%BA%E4%BB%BB%E5%8A%A1%E4%B8%89%E4%BB%B6%E5%A5%97" class="hash-link" aria-label="个人任务三件套的直接链接" title="个人任务三件套的直接链接">​</a></h3>
<ol>
<li>滴答清单：安排任务、定时任务</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210643-1us241-4413626f2afa4ee0bce2c02bf23dffea.png" alt="" class="img_ev3q"></p>
<ol start="2">
<li>Flomo：记录灵感，针对任务过程 or 结果进行复盘</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210644-WxT4l6-56918f2910ce4913b91f46a1e127e6be.png" alt="" class="img_ev3q"></p>
<ol start="3">
<li>FlowUs：复盘产出，读书笔记</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210644-OAz4yK-c340a23af14b4e95a8c46d33b734f22a.png" alt="" class="img_ev3q"></p>
<p>为什么选择这一工具套件的原因？</p>
<ol>
<li>全为国内应用，无需梯子，避免网络波动的着急时刻</li>
<li>支持跨平台，移动端、PC 端、Web 端、小程序全面覆盖</li>
<li>好用，平替 Todoist、Notion 等</li>
</ol>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="revezone">Revezone<a href="https://wukaipeng.com/blog/mac-application#revezone" class="hash-link" aria-label="Revezone的直接链接" title="Revezone的直接链接">​</a></h3>
<p>平时需要画一些流程图，会使用 Excalidraw 涂鸦式画图工具，可惜它不支持中文字体。</p>
<p>Revezone 相当于是 Excalidraw 的汉化版，但更进一步，集成了 Tldraw、类 Notion 笔记功能，并且提供 Mac 客户端。</p>
<p>目前开源免费，正在 alpha 公测阶段，可以 try try。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210645-33gwzQ-785c32c8779942eba21b42b1fd306bf0.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="录屏">录屏<a href="https://wukaipeng.com/blog/mac-application#%E5%BD%95%E5%B1%8F" class="hash-link" aria-label="录屏的直接链接" title="录屏的直接链接">​</a></h3>
<ol>
<li>OBS：<strong>专业录制</strong>，如果有需要录课等重量用途，就选他。如果只是一些轻量的录制可以用飞书的 Gif 录制</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210645-F0AGU9-94b49f51d76c4aa7a0c228fd458288bd.png" alt="" class="img_ev3q"></p>
<ol start="2">
<li>Screen Studio：可以制作非常丝滑的<strong>操作视频</strong></li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210646-QUbIXk-83a896837e8740aba7a4b06ecf4f7182.png" alt="" class="img_ev3q"></p>
<ol start="3">
<li>KeyCastr，<strong>显示输入的按键</strong>，开源免费</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210646-UYmWqE-ca3f7055bb1b439f8fe261663ed3d8d0.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="utools">uTools<a href="https://wukaipeng.com/blog/mac-application#utools" class="hash-link" aria-label="uTools的直接链接" title="uTools的直接链接">​</a></h3>
<p>确实是新一代的效率工具平台，万物皆插件，需要什么小功能都可以在上面找到</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210647-G0gD5l-4417c34efa954d548b48a0bba8c683ca.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="贝锐向日葵">贝锐向日葵<a href="https://wukaipeng.com/blog/mac-application#%E8%B4%9D%E9%94%90%E5%90%91%E6%97%A5%E8%91%B5" class="hash-link" aria-label="贝锐向日葵的直接链接" title="贝锐向日葵的直接链接">​</a></h3>
<p>需要远程操作另一台计算机，贝锐向日葵推荐，free 版基本满足需求，不过总会弹一些小广告。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210647-g9Ckd5-d412bf92bf4f495a97d021029f4a4d43.png" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="开发">开发<a href="https://wukaipeng.com/blog/mac-application#%E5%BC%80%E5%8F%91" class="hash-link" aria-label="开发的直接链接" title="开发的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="fork">Fork<a href="https://wukaipeng.com/blog/mac-application#fork" class="hash-link" aria-label="Fork的直接链接" title="Fork的直接链接">​</a></h3>
<p>一个免费的 <strong>Git 可视化工具</strong>，挺形象的「叉子」。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210648-4GzQpY-47e3688e9ce346d1a9d0bddb9a84118f.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="warp">Warp<a href="https://wukaipeng.com/blog/mac-application#warp" class="hash-link" aria-label="Warp的直接链接" title="Warp的直接链接">​</a></h3>
<p>新生的终端应用，支持基本<strong>补全、记忆</strong>功能，还有 <strong>AI 加持</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210649-E6kcQ2-7897db6aac2e4af38071913bd0ac10c1.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="zed">Zed<a href="https://wukaipeng.com/blog/mac-application#zed" class="hash-link" aria-label="Zed的直接链接" title="Zed的直接链接">​</a></h3>
<p>Jetbrain、VSCode 等的就不推荐了，这些都是有名的代码编辑器了。</p>
<p>就推荐下【 Zed 】，基于 Rust 的新一代编辑器，<strong>启动、输入速度非常快</strong>。</p>
<p>Zed 刚起步，后续持续观察。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210650-AYMEK4-c085d2b1b9514e2082a6a4a7862859a1.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="数据库可视化">数据库可视化<a href="https://wukaipeng.com/blog/mac-application#%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8F%AF%E8%A7%86%E5%8C%96" class="hash-link" aria-label="数据库可视化的直接链接" title="数据库可视化的直接链接">​</a></h3>
<p>数据库可视化老大 Navicat，这个大家比较熟悉了，<strong>最近终于推出了免费版的 Navicat Premium Lite</strong>，Navicat Premium Lite 作为 Navicat Premium 17 的精简版，基本功能都在。</p>
<p>官方说【它可满足广大初级用户和非商业用途的基础数据库管理需求】（可能是盗版太太太多了 🤣）</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210650-eBj8QB-8e84b1dcb4464d0d9c1c09ae5f615a20.png" alt="" class="img_ev3q"></p>
<p>如果只需要一个轻量的数据库可视化操作，那么推荐一下【SQL Pro Studio】</p>
<p>虽然没有 Navicat 那么强大，但好在是 Mac <strong>原生应用，性能不错</strong>，基本的操作（筛选、排序）都能满足，高级的能力需要写一下 SQL</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210650-WJR0oz-4709cbe11acf40b1b18d95ff94936b77.png" alt="" class="img_ev3q"></p>
<p>如果使用的是 Postgres 数据库，那么可以使用 Postico 2，<strong>专门用于 Postgres</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210651-7N6ZB4-47f982fb43da490d8f0a194e5ec3131e.png" alt="" class="img_ev3q"></p>
<p>Redis 可视化推荐 Another Redis Desktop Manager 或者 Redis Insight，这两款各有优缺，可以搭配使用</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210651-0hoGFm-5a83331e65144994912b174052053ddb.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210651-YzRXoz-0c776f59d9234e089863624098f78c40.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="typora--upic">Typora &amp; uPic<a href="https://wukaipeng.com/blog/mac-application#typora--upic" class="hash-link" aria-label="Typora &amp; uPic的直接链接" title="Typora &amp; uPic的直接链接">​</a></h3>
<p>写 Markdown 笔记的时候，Typora 一直是我的<strong>白月光</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210652-zVksaN-e8ce678769dd46e08016711756b85e08.png" alt="" class="img_ev3q"></p>
<p>Typora 本身不支持图片，于是自己用服务器搭了个图床，目前<strong>搭配 uPic 上传图片</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210652-imvnSK-b55ee28c88db44c68f5ef9cf8301bda1.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="he3">He3<a href="https://wukaipeng.com/blog/mac-application#he3" class="hash-link" aria-label="He3的直接链接" title="He3的直接链接">​</a></h3>
<p><strong>面向开发者的工具箱</strong>，集合多款的开发专用小工具，也有一些基础的视频剪辑、图片裁剪、音频转换等工具。
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/06/26-210653-SOsmMo-29c218b5803441bf8fc4281b39d8dbac.png" alt="" class="img_ev3q"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 挖到谷歌一个 XSS 漏洞]]></title>
            <link>https://wukaipeng.com/blog/google-xss-bug</link>
            <guid>https://wukaipeng.com/blog/google-xss-bug</guid>
            <pubDate>Sat, 11 May 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[程序员 Matan 挖到了一个 XSS 漏洞并报告给谷歌，奖励 3133.7 美金（约合人民币 22666 元）]]></description>
            <content:encoded><![CDATA[<p>程序员 Matan 挖到了一个 <a href="https://matan-h.com/common-google-xss" target="_blank" rel="noopener noreferrer">XSS 漏洞</a>并报告给谷歌，奖励 3133.7 美金（约合<strong>人民币 22666 元</strong>）</p>
<p>这是谷歌 Bug Hunter 的奖励规则：
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145858-1ntD9Q-693fe8f451cb41d09db69c3b6f7502d2.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>👉 图片来自 <a href="https://bughunters.google.com/about/rules/google-friends/6625378258649088/google-and-alphabet-vulnerability-reward-program-vrp-rules" target="_blank" rel="noopener noreferrer">https://bughunters.google.com/about/rules/google-friends/6625378258649088/google-and-alphabet-vulnerability-reward-program-vrp-rules</a></p>
</blockquote>
<p>事情起因是这样的，Matan 看到了一篇<a href="https://www.rcesecurity.com/2017/03/ok-google-give-me-all-your-internal-dns-information/" target="_blank" rel="noopener noreferrer">揭露谷歌 SSRF 漏洞的文章</a>，文章中提到谷歌的这么一个网站</p>
<p>📍 <a href="https://toolbox.googleapps.com/" target="_blank" rel="noopener noreferrer">https://toolbox.googleapps.com</a></p>
<p>于是便开始探寻，先是<strong>查看 robots.txt 文件</strong>：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">#apps-toolbox</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">User-Agent: *</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/browserinfo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/checkmx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/dig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/har_analyzer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/loganalyzer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/loggershark</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/messageheader</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/recovery</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/useragent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/other_tools</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/encode_decode</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Allow: /apps/screen_recorder</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Disallow: *</span><br></span></code></pre></div></div>
<blockquote>
<p>robots.txt 是一份存在于网站根目录的文件，它会告诉网络爬虫应该哪些页面可以爬，哪些不可以，以此<strong>避免网站被爬虫过度请求</strong>，造成请求负担</p>
</blockquote>
<p>在 robots.txt 文件中，一个链接对应一个工具网页</p>
<p>但是有一个例外，<code>/apps/recovery</code> 是不能被直接访问的</p>
<p>在经过简单搜索后，发现它存在子页面</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">recovery/domain_in_use</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">recovery/form</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">recovery/ownership</span><br></span></code></pre></div></div>
<p>这些子页面都能够接收多个 URL 参数，比如</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">recovery/domain_in_use?visit_id=xxx&amp;user=xxx&amp;domain=xxx&amp;email=xxx</span><br></span></code></pre></div></div>
<p>如果输入这条<strong>继续跳转链接</strong>的话</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">https://toolbox.googleapps.com/apps/recovery/ownership?domain=example.com&amp;email=email@example.com&amp;case=45500368&amp;continue=/apps/recovery/...</span><br></span></code></pre></div></div>
<p>这条链接包含参数 <code>domain=example.com</code>，注意还有一个参数是 <code>continue=/apps/recovery/...</code></p>
<p>输入该继续跳转链接会得到提示：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145858-ujXByq-ecb74bb86e2b4d8e993acc17f83a4067.png" alt="" class="img_ev3q"></p>
<p>在这里，发现了问题，CONTINUE 按钮的链接居然是来自于 <code>continue</code> 参数</p>
<p>Matan 验证了下，注入 JavaScript 脚本代码：<code>.../continue=javascript:alert(document.domain)</code></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145858-vGGmIx-50e529c68ed04cdc93fdf4d140613673.png" alt="" class="img_ev3q"></p>
<p>成功执行 ✅</p>
<p>这个网站没有 CSP 安全策略，也没有任何防护措施，因此可以从任意外部获取资源</p>
<p>继续尝试加载外部恶意脚本，该恶意脚本用于获取用户 IP 地址：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">.../continue=javascript:fetch(%27https://api.ipify.org?format=json%27).then(response=%3Eresponse.text()).then(data=%3E{alert(data);%20})</span><br></span></code></pre></div></div>
<p>也是成功执行 ✅</p>
<p>到这里，可以确定存在 XSS 漏洞。</p>
<p>回顾一下 XSS 知识</p>
<p>XSS（Cross-Site Scripting），<strong>跨站脚本攻击</strong>，没有做好校验，相信用户的输入，接收了攻击者的恶意输入（一般是 JavaScript 脚本代码），导致该恶意输入在其他用户页面上执行。XSS 一般分为三种：</p>
<ol>
<li><strong>存储型</strong>：恶意输入被永久储存在了后台服务中，无论用户什么时候打开网站，拿到该恶意输入，浏览器就会执行</li>
<li><strong>反射型</strong>：恶意输入内嵌在 URL 或者其他输入中，立即被后台转发，用户只要访问构造好的 URL，浏览器就会执行</li>
<li>基于 DOM：攻击者操作用户的 DOM 结构，进而执行恶意代码</li>
</ol>
<p>上诉 Matan 的例子，就是一个很典型的反射型 XSS。</p>
<p>但这个 XSS 太低级了，以至于 Matan 本人都难以置信，毕竟谷歌技术是业界有名的</p>
<p>不过谷歌惯常喜欢用自研框架，而这些框架没有做好安全策略，产品翻车也是难免。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Zed，有望打败 VS Code 吗？]]></title>
            <link>https://wukaipeng.com/blog/zed</link>
            <guid>https://wukaipeng.com/blog/zed</guid>
            <pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[先说结论，不行。]]></description>
            <content:encoded><![CDATA[<p>先说结论，<strong>不行</strong>。</p>
<p>Zed，又一款新起的文本代码编辑器</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-kwRP3O-7a5ca12c8ed94bb39d199acc4ff2e765.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>👉 <a href="https://zed.dev/" target="_blank" rel="noopener noreferrer">https://zed.dev</a></p>
</blockquote>
<p>今年一月二十四号正式开源，短短不到三个月，GitHub 上已经冲上 3 万 star</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-59Vms8-d22ed923492047229cef9433c8d1902c.png" alt="" class="img_ev3q"></p>
<p>正如 Zed 的口号所说「Code at the speed of thought <strong>以思考的速度编码</strong>」</p>
<p>实际体验下来，Zed 确实会比 VS Code 丝滑</p>
<p>⬇️ Zed</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-38CbSu-ccdb750a0050401eaa214b24dec24d34.gif" alt="" class="img_ev3q"></p>
<p>⬇️ VS Code</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-Q779zH-0ad8f4f9d6f6498fbc9280108868383d.gif" alt="" class="img_ev3q"></p>
<p>官网也给出了打字输入性能对比：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-BsiIPC-44c40715983f41649dd9716b0da58a2a.png" alt="" class="img_ev3q"></p>
<p>输入字母 <em>z</em> 并显示到屏幕，Zed 仅需 58 毫秒，而 VS Code 需要 97 毫秒</p>
<p>Zed 比 VS Code 快了 1.4 倍</p>
<p>在输入性能方面，<strong>Zed 胜出</strong></p>
<p>其次就是 Zed 主打的另一个核心功能，<strong>多用户协同编程</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-QHUkDM-88a9604967354cd0a734e67faf6ab6a3.gif" alt="" class="img_ev3q"></p>
<p>额说实话，这个功能暂时想不到很好的落地使用场景。</p>
<hr>
<p>到目前为止，Zed <strong>仅仅</strong>是一个不错的文本编辑器。</p>
<p>甚至可以说，Zed <strong>实质上并没有重大的突破</strong>，属于自嗨产品。</p>
<p>Zed 宣传的高性能，并没有质的飞跃，很难打到用户的马屁上。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-bsdUOP-619bc0847b134e49b16258e3b67b077f.png" alt="" class="img_ev3q"></p>
<p>「58毫秒」和「97毫秒」两个差距并不大</p>
<p>实际开发都知道，<strong>编程的瓶颈并不在于输入速度</strong>。</p>
<p>另外是多用户协同，目前看这个场景不友好</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-Y7Qt5G-f8140739c59b46c3810e909ecc0259b3.gif" alt="" class="img_ev3q"></p>
<p>如果是文档协同，国内的飞书文档、腾讯文档等哪一个不是佼佼者，按着 Zed 锤。</p>
<p>如果是代码协同，显然 Git 才是主流。</p>
<hr>
<p>Zed 太年轻，目前很基础的 markdown 预览都没有实现</p>
<p>VS Code 珠玉在前，用开源、插件化形成的护城河，一开放拥有大批拥趸</p>
<p>而 Zed 虽然同样有插件机制，但是能<strong>指望多少人贡献呢？</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-rnuMm7-985738d95568472a994ddcd32199c101.png" alt="" class="img_ev3q"></p>
<p>《重来》一书讲到，<strong>第一次创业失败的人，第二次创业失败概率一样大</strong></p>
<p>Zed 的团队原先做过 Atom 编辑器，而现在 Atom 名存实亡</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-APxr8r-ed69b1e3b82344eda91a2c1ed3411dcc.png" alt="" class="img_ev3q"></p>
<p>团队做 Atom 失败过，而卷土重来的 Zed，<strong>还不行</strong>。</p>
<p>Zed 大概率能够圈住一部分用户，但不会成为领域的成功。</p>
<hr>
<p>Zed 如何能破局呢？最重要的还是要<strong>顺势而为</strong></p>
<p>想想 VS Code 当时，互联网的繁荣，带动开源领域的发展，Eclipses 老旧、Jetbrains 高昂收费，前端分工细化，急需轻量的编辑器，这些都是 VS Code 的势头。</p>
<p>而目前 Zed 最好的势头，显而易见，就是 <strong>AI 方向</strong></p>
<p>而 Zed 目前显然支持不足，仅有 Copilot 代码不足和 Chat 能力</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/05/11-145945-wS88cP-f70ae1454c564ef6a06864f835cdc720.gif" alt="" class="img_ev3q"></p>
<p>而这些 VS Code 不仅有，而且功能更加完善。</p>
<p>Zed 团队应该思考下了，要做一款怎么样的编辑器，适应目前的 AI 潮流，开创新的赛道。</p>
<p>如果继续安于微不足道的性能提升、垂直的协同，继续在垂直赛道内卷，那我祝你成功。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 100% 压榨 GitHub Copilot 指南]]></title>
            <link>https://wukaipeng.com/blog/github-copilot</link>
            <guid>https://wukaipeng.com/blog/github-copilot</guid>
            <pubDate>Sun, 07 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[早在 OpenAI 推出 ChatGPT 之前，OpenAI 就已经和 GitHub 合作推出了 GitHub Copilot。]]></description>
            <content:encoded><![CDATA[<p>早在 OpenAI 推出 ChatGPT 之前，OpenAI 就已经和 GitHub 合作推出了 GitHub Copilot。</p>
<blockquote>
<p>下文统一简称 Copilot</p>
</blockquote>
<p>Copilot 当时在编程圈引起轰动。</p>
<p>当时激进的说法是：<strong>Copilot 将会取代程序员</strong>。</p>
<p>三年之后，冷静下来，Copilot 并没有如愿替代程序员。</p>
<p>相反，它是一个提高效率的强大助手，掌握它，可以让你的<strong>编程效率提高数倍</strong>。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-234638-MyWTBn-download.png" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="copilot-的价格">Copilot 的价格<a href="https://wukaipeng.com/blog/github-copilot#copilot-%E7%9A%84%E4%BB%B7%E6%A0%BC" class="hash-link" aria-label="Copilot 的价格的直接链接" title="Copilot 的价格的直接链接">​</a></h2>
<p>国内开发者对价格相对来说还是比较敏感的。</p>
<p>Copilot 官方有两种订阅方式，一个是<strong>个人版</strong>，一个是<strong>团队版</strong>。</p>
<p>团队就不说了，有公司支持就不需要考虑这一茬。</p>
<p>个人版的价格有两种：</p>
<ol>
<li>月度订阅，每月 10 美刀，<strong>人民币 72.37 元</strong></li>
<li>年度订阅，每年 100 美刀，<strong>人民币 723.7 元</strong></li>
</ol>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-234839-d3CXp0-image-20240412234839346.png" alt="" class="img_ev3q"></p>
<p>个人版会有 30 天的免费适用期，可以先试用一下。</p>
<p>GitHub 比较友好，<strong>支持国内的信用卡绑定支付</strong>，招商、广发、建行等都可以。</p>
<p>非官方渠道的话，比如 x 宝、uTools 插件等，可能会不稳定，但胜在价格便宜。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="copilot-全家桶">Copilot 全家桶<a href="https://wukaipeng.com/blog/github-copilot#copilot-%E5%85%A8%E5%AE%B6%E6%A1%B6" class="hash-link" aria-label="Copilot 全家桶的直接链接" title="Copilot 全家桶的直接链接">​</a></h2>
<p>Copilot 使用很简单，在 VSCode/JetBrains 上安装对应的插件，然后登录 GitHub 账号即可。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-181521-InDV4W-12-180931-cqLDKX-image-20240412180930744.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>👉 VSCode: <a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot" target="_blank" rel="noopener noreferrer">https://marketplace.visualstudio.com/items?itemName=GitHub.copilot</a>
👉 JetBrains: <a href="https://plugins.jetbrains.com/plugin/17718-github-copilot" target="_blank" rel="noopener noreferrer">https://plugins.jetbrains.com/plugin/17718-github-copilot</a></p>
</blockquote>
<p>安装后，输入代码时，Copilot 会自动提示代码，按 <code>Tab</code> 键即可补全。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-181423-r0m99D-212964557-8d832278-61bb-4288-a8a7-47f35859e868.gif" alt="" class="img_ev3q"></p>
<p>JetBrains 的 Copilot 插件集成提供侧边栏 Chat 功能：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-182530-ZLDeBh-image-20240412182529852.png" alt="" class="img_ev3q"></p>
<p>对于 VSCode 用户来说，如果要开启侧边栏 Chat 功能，需要额外安装 Copilot Chat 插件：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-182139-JZOxxA-image-20240412182138873.png" alt="" class="img_ev3q"></p>
<p>安装之后，侧边栏菜单会多了一个「Chat」项，和 ChatGPT 一样的聊天界面：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-182250-GevSkt-image-20240412182250704.png" alt="" class="img_ev3q"></p>
<p>VSCode 确实会稍微麻烦一点，不过有个优势</p>
<p>就是新的 Copilot 的新功能都会优先上架 VSCode，比如 Copilot Voice</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-212938-WOOCt5-12-212913-8ez2BA-image-20240412212912912.png" alt="" class="img_ev3q"></p>
<p>在 VSCode 装一下这个插件 👇 就能体验<strong>动嘴编程</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-213107-dMajyo-image-20240412213107161.png" alt="" class="img_ev3q"></p>
<p>虽然目前仅支持英文，但相信多语言很快会支持上</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-213300-UPF36A-63279c01-3941-46c5-bf51-284fbc31fbfe.gif" alt="Speech to text in Visual Studio Code Chat" class="img_ev3q"></p>
<p>不过就算只支持英语，相信从小就开始学英语的中国开发者们，英语能力都非常好</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-213812-4hp6KE-R.jpeg" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="copilot-代码补全">Copilot 代码补全<a href="https://wukaipeng.com/blog/github-copilot#copilot-%E4%BB%A3%E7%A0%81%E8%A1%A5%E5%85%A8" class="hash-link" aria-label="Copilot 代码补全的直接链接" title="Copilot 代码补全的直接链接">​</a></h2>
<p>除了 Copilot 自动触发外，我们也可以使用 <code>Option</code> + <code>\</code> 去主动触发代码补全。</p>
<blockquote>
<p>Window 为 <code>Alt</code> + <code>\</code></p>
</blockquote>
<p>虽然主动快捷键比较少用到，但网络波动时可以测试下 Copilot。</p>
<p>触发代码补全后，按下 <code>Tab</code> 接受会接受全部的代码，但有时候我们只需要一部补全代码</p>
<p>可以使用 <code>Command</code> + → 去一步步接受补全代码：</p>
<blockquote>
<p>Windows 为 <code>Ctrl</code> + →</p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-221048-rhh5pH-20240412221008_rec_-convert.gif" alt="" class="img_ev3q"></p>
<p>如果对当前的补全代码不满意，可以按 <code>Option</code> + <code>]</code> 或者 <code>Option</code> + <code>[</code> 去切换下一个或者上一个补全代码：</p>
<blockquote>
<p>Windows 为 <code>Alt</code> + <code>]</code> 或者 <code>Alt</code> + <code>[</code></p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-221510-ZAlVae-20240412221442_rec_-convert.gif" alt="" class="img_ev3q"></p>
<p>对于 VSCode 来说，按下 <code>Command</code> + <code>I</code> 代码中唤起 Copilot Editor：</p>
<blockquote>
<p>Windows 为 <code>Ctrl</code> + <code>I</code></p>
</blockquote>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-222404-6ICwrS-20240412222305_rec_-convert.gif" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="copilot-chat-使用">Copilot Chat 使用<a href="https://wukaipeng.com/blog/github-copilot#copilot-chat-%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="Copilot Chat 使用的直接链接" title="Copilot Chat 使用的直接链接">​</a></h2>
<p>Copilot Chat 相当于为编辑器直接配置一个 GPT-4，可以方便快速地快速问答项目问题：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-222909-NjtEzI-image-20240412222909416.png" alt="" class="img_ev3q"></p>
<p>对于 JetBrains IDEs，需要在文件右键显示引用：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-223012-8owLSw-image-20240412223012255.png" alt="" class="img_ev3q"></p>
<p>JetBrains IDEs 目前比较笨，需要手动右键显式指明哪个文件，并且引用整个文件</p>
<p>而 VSCode 不需要显示指明，它会自行判断是引用文件全部，还是仅引用鼠标选中的：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-223257-kmNJYr-image-20240412223257582.png" alt="" class="img_ev3q"></p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="copilot-的一些小幸福">Copilot 的一些小幸福<a href="https://wukaipeng.com/blog/github-copilot#copilot-%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B0%8F%E5%B9%B8%E7%A6%8F" class="hash-link" aria-label="Copilot 的一些小幸福的直接链接" title="Copilot 的一些小幸福的直接链接">​</a></h2>
<p>再来说一下 Copilot 的一些痒点功能，比如直接生成 Commit Message：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-223743-34Opnl-20240412223717_rec_-convert.gif" alt="" class="img_ev3q"></p>
<p>生成 Git Commit 这点确实很方便，它会检测所有文件变更，并且生成合适的 Message。</p>
<p>另外是重命名变量，不过目前这个功能不太稳定，暂时略过。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="copilot-cli">Copilot CLI<a href="https://wukaipeng.com/blog/github-copilot#copilot-cli" class="hash-link" aria-label="Copilot CLI的直接链接" title="Copilot CLI的直接链接">​</a></h2>
<p>Copilot CLI 现在已经全面开放 Copilot CLI，可以在命令行中使用 Copilot。</p>
<p>比如我们可以让 copilot 解释 <code>sudo apt-get</code> 这条命令的意思：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/12-225423-uOLDTd-image-20240412225423062.png" alt="" class="img_ev3q"></p>
<p>不过目前 CLI 还是挺笨的，不如使用 Warp 的 AI 功能，参考之前文章 👉</p>
<p><a href="https://mp.weixin.qq.com/s/l9IZeitz8zX7GjZFAR5xFg" target="_blank" rel="noopener noreferrer">好看好用 + 免费 AI 能力的终端工具，推荐 Warp！</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 三个开发者，支撑一万亿的活跃使用量]]></title>
            <link>https://wukaipeng.com/blog/sqlite</link>
            <guid>https://wukaipeng.com/blog/sqlite</guid>
            <pubDate>Tue, 02 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[对于很多开发者来说，SQLite 一定不陌生。]]></description>
            <content:encoded><![CDATA[<p>对于很多开发者来说，SQLite 一定不陌生。</p>
<p>也知道它很强，但是没想到居然这么强。</p>
<p>SQlite 目前超一万亿（<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi>e</mi><mn>12</mn></mrow><annotation encoding="application/x-tex">1e12</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span><span class="mord mathnormal">e</span><span class="mord">12</span></span></span></span>）的活跃使用量。</p>
<p>它主要用于：</p>
<table><thead><tr><th>平台</th><th>包含SQLite</th></tr></thead><tbody><tr><td>移动设备</td><td>每一台安卓设备，每一台 iPhone 和 iOS 设备</td></tr><tr><td>计算机</td><td>每一台 Mac，每一台 Windows10 机器</td></tr><tr><td>网络浏览器</td><td>每一款 Firefox、Chrome 和 Safari网络浏览器</td></tr><tr><td>通讯应用</td><td>每一个 Skype 实例</td></tr><tr><td>媒体应用</td><td>每一个 iTunes 实例，每一个 Dropbox 客户端</td></tr><tr><td>财务软件</td><td>每一款 TurboTax 和 QuickBooks</td></tr><tr><td>编程语言</td><td>PHP 和 Python</td></tr><tr><td>家庭娱乐</td><td>大多数电视机和机顶盒</td></tr><tr><td>汽车</td><td>大多数汽车多媒体系统</td></tr><tr><td>其他</td><td>无数百万其他应用程序</td></tr></tbody></table>
<blockquote>
<p>👉 表格来源于：<a href="https://www.sqlite.org/mostdeployed.html" target="_blank" rel="noopener noreferrer">https://www.sqlite.org/mostdeployed.html</a></p>
</blockquote>
<p>而 SQLite 的全部开发者，也就三个人：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/21-091841-ZHGYCj-9cdd7881a86d467087b22b51435d8e47.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>👉 图片来源于：<a href="https://www.sqlite.org/crew.html" target="_blank" rel="noopener noreferrer">https://www.sqlite.org/crew.html</a></p>
</blockquote>
<ul>
<li><em><strong>D. Richard Hipp</strong></em> ：<strong>2000 年 5 月 29 日开始 SQLite 项目</strong>，并继续担任项目架构师。理查德在北卡罗来纳州夏洛特出生、生活和工作。他拥有佐治亚理工学院（电子工程硕士学位，1984 年）和杜克大学（博士学位，1992 年）学位，并且是咨询公司 Hwaci 的创始人。</li>
<li><em><strong>Dan Kennedy</strong></em> ：澳大利亚人，目前居住在东南亚。他拥有昆士兰大学计算机系统工程学位，曾在多个领域工作过，包括工业自动化、计算机图形和嵌入式软件开发。<strong>Dan 是主要贡献者自 2002 年起使用 SQLite。</strong></li>
<li><em><strong>Joe Mistachkin</strong></em>（发音为“miss-tash-kin”）：软件工程师，也是 Tcl/Tk 的维护者之一。他也是 TclBridge 组件和 Eagle 脚本语言的作者。他自 1994 年以来一直在软件行业工作。</li>
</ul>
<p>另外一件有趣的事情是，SQLite 不接受任何外来的代码贡献。</p>
<p>也就是说，SQLite 开源，但是并不开放代码贡献。</p>
<p>在 SQLite 的版权声明有提到：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/04/21-091841-PzdJPG-550640d42a8244cd81e7f3887937414e.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>👉 图片来源于：<a href="https://www.sqlite.org/copyright.html" target="_blank" rel="noopener noreferrer">https://www.sqlite.org/copyright.html</a></p>
</blockquote>
<p>很多时候，都不得不感慨<strong>软件的边际成本</strong>。</p>
<p>一份代码，可以分发给十个人用，也可以给<strong>十亿个人使用</strong>。</p>
<p>三个开发者，就支撑一万亿的活跃使用量。</p>
<p>SQLite 创造的价值，无与伦比，科技改变世界。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="references">REFERENCES<a href="https://wukaipeng.com/blog/sqlite#references" class="hash-link" aria-label="REFERENCES的直接链接" title="REFERENCES的直接链接">​</a></h2>
<ul>
<li><a href="https://x.com/iavins/status/1774464622067679738" target="_blank" rel="noopener noreferrer">https://x.com/iavins/status/1774464622067679738</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 好看好用 + 免费 AI 能力的终端工具，推荐 Warp]]></title>
            <link>https://wukaipeng.com/blog/warp</link>
            <guid>https://wukaipeng.com/blog/warp</guid>
            <pubDate>Thu, 14 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[最近 Warp 声名鹊起，很多人推荐这款终端工具，于是便下载体验一番。]]></description>
            <content:encoded><![CDATA[<p>最近 Warp 声名鹊起，很多人推荐这款终端工具，于是便下载体验一番。</p>
<p>经过几天的深度体验，结论是<strong>确实可以平替原先的 iTerm</strong>。</p>
<p>Warp 颜值可以，看着舒服，满足了前端工程师花哨的需求。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-wDr058-6a2ea263bd1a46a380f28df4259ebeff.png" alt="" class="img_ev3q"></p>
<p>有<strong>多种主题</strong>可选：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-2PfILQ-a9569bc5082343adbf4e4e847730d3cc.png" alt="" class="img_ev3q">
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-fL9KDD-0c8d9d9ed8764500bb8a75092a40e76a.png" alt="" class="img_ev3q"></p>
<p>Warp 直接提供<strong>命令提示</strong>功能，不需要像  iTerm 那么折腾：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-3qOEJB-b79760d2485446dfb7becf190ab2754b.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-IbGPZP-fdf4f542aacf41efb559a4b27187c13a.png" alt="" class="img_ev3q"></p>
<p>最好用的，当然是 AI 能力，在右上角即可打开「<strong>Warp AI</strong>」，忘记命令了，可以直接 chat：
<img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-UI5xRL-8f8b548ac1e24360bd0fdb3b97ae9f60.png" alt="" class="img_ev3q"></p>
<p>对于 Free 用户，每天可以有 <strong>20 次免费额度</strong>，这对于个人来说足够了：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-cN4fiP-ad732ea288ed44c284c2c9e9f647a0b6.png" alt="" class="img_ev3q"></p>
<p>Warp 还提供叫做「<strong>Warp Drive</strong>」，可以在团队中<strong>保存和分享</strong>命令：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075844-ZY4wRr-05392aaaecde4ad7883935acc81ba2d1.png" alt="" class="img_ev3q"></p>
<p>目前 Warp <strong>仅支持 Mac 和 Linux</strong>，Windows 也很快会支持：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/03/19-075845-X9ZqN9-0fb29ce2b4954f66915ada8a7fb972c2.png" alt="" class="img_ev3q"></p>
<p>如果觉得 Warp 不错，不妨一试：
👉 地址：<a href="https://app.warp.dev/referral/Q9ZVGQ" target="_blank" rel="noopener noreferrer">https://app.warp.dev/referral/Q9ZVGQ</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[不花一分钱，如何在 Mac 上跑 Windows（M1/M2 版）]]></title>
            <link>https://wukaipeng.com/blog/utm-fusion</link>
            <guid>https://wukaipeng.com/blog/utm-fusion</guid>
            <pubDate>Thu, 15 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[这是在 MacOS M1 上体验最新 Windows11 的效果：]]></description>
            <content:encoded><![CDATA[<p>这是在 MacOS M1 上体验最新 Windows11 的效果：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/16-141107-GcAOad-image-20240216112510958.png" alt="" class="img_ev3q"></p>
<p><strong>VMware Fusion</strong>，可以运行 Windows、Linux 系统，个人使用 licence 免费</p>
<p>安装流程见 👉 <a href="https://zhuanlan.zhihu.com/p/452412091" target="_blank" rel="noopener noreferrer">https://zhuanlan.zhihu.com/p/452412091</a></p>
<p>从申请 Fusion licence 到下载镜像，再到安装避坑</p>
<p>这篇文章讲非常详细</p>
<hr>
<p>Fusion 对 Windows11 支持非常好，不支持其他版本</p>
<p>这里推荐 <strong>UTM</strong>，这是 UTM 运行 Windows7 的效果：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/16-141110-fQTSMS-image-20240216113429868.png" alt="" class="img_ev3q"></p>
<p>UTM 也是一款免费虚拟系统运行软件</p>
<p>这是官网 👉 <a href="https://mac.getutm.app/" target="_blank" rel="noopener noreferrer">https://mac.getutm.app</a></p>
<blockquote>
<p>注意，UTM 从官网可直接下载</p>
<p>但 App Store 下载需要付费，如果想要赞助 UTM，可以选择付费下载</p>
</blockquote>
<br>
<p>UTM 安装比较简单</p>
<p>先下载想要的镜像，镜像地址可从 MSDN 下载 👉 <a href="https://msdn.itellyou.cn/" target="_blank" rel="noopener noreferrer">https://msdn.itellyou.cn</a></p>
<p>接着从 UTM 中下载对应配置文件 <a href="https://mac.getutm.app/gallery/" target="_blank" rel="noopener noreferrer">https://mac.getutm.app/gallery/</a></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/16-114203-b8kslZ-image-20240216114203621.png" alt="" class="img_ev3q"></p>
<p>下载之后是一个 .utm 后缀的文件，然后打开该文件</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/16-114350-0BIAqS-image-20240216114350208.png" alt="" class="img_ev3q"></p>
<p>打开之后需要改系统镜像路径</p>
<p>在右上角改成对应的下载好的系统镜像即可：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/16-114624-j5OEoX-image-20240216114512243.png" alt="" class="img_ev3q"></p>
<p>改后重新启动即可</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 拯救 VSCode，试试这款高颜值代码字体]]></title>
            <link>https://wukaipeng.com/blog/monaspace</link>
            <guid>https://wukaipeng.com/blog/monaspace</guid>
            <pubDate>Wed, 07 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Monaspace，由 GitHub 开源的代码字体，包含 5 种变形字体的等宽代码字体家族，颜值 ++。]]></description>
            <content:encoded><![CDATA[<p><strong>Monaspace</strong>，由 GitHub 开源的代码字体，包含 5 种变形字体的等宽代码字体家族，颜值 ++。</p>
<p>这 5 种字体分别是：</p>
<p>1️⃣ <strong>Radon 手写风格字体</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-112838-Kt1yjm-0b1515bdd59e4a13b4ffeb2c88bd01c2.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-112939-l6cBdx-e83c131bbb80477384e8de796f51d3de-20240215112939793.png" alt="" class="img_ev3q"></p>
<p>2️⃣ <strong>Krypton 机械风格字体</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-112905-v0qvxz-ff6016bf0bd54118aa333a3d398244a0.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-112850-Y0X5g2-54792f1e3edf42b98aa1e2771547be6e.png" alt="" class="img_ev3q"></p>
<p>3️⃣ <strong>Xenon 衬线风格字体</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-112855-0gnovN-fd0dde46d6b540d8b9942d17199810d5.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-112954-QEsed4-617ffb68ff114d7da9c3b96f6e35481d.png" alt="" class="img_ev3q"></p>
<p>4️⃣ <strong>Argon 人文风格字体</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-113000-1YCib3-a87a10f92674473a93a220287c2b46f8.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-113006-0T9UAQ-b3b81cd9c0804e2a8f8ea22f0039f278.png" alt="" class="img_ev3q"></p>
<p>5️⃣ <strong>Neon 现代风格字体</strong></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-113013-t2fUUG-468099c48e554bfdb3b5b2922de3e4b5.png" alt="" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-113017-4BjVLg-b73ab6b059cd4332a31a900abc284087.png" alt="" class="img_ev3q"></p>
<p>👉 项目地址：<a href="https://github.com/githubnext/monaspace?tab=readme-ov-file#coding-ligatures" target="_blank" rel="noopener noreferrer">https://github.com/githubnext/monaspace?tab=readme-ov-file#coding-ligatures</a></p>
<p>下载方式</p>
<p><strong>MacOS</strong></p>
<p>使用 brew 安装：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">brew tap homebrew/cask-fonts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">brew </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> font-monaspace</span><br></span></code></pre></div></div>
<p><strong>Windows</strong></p>
<p>下载该文件：<a href="https://github.com/githubnext/monaspace/tree/main/fonts/otf" target="_blank" rel="noopener noreferrer">https://github.com/githubnext/monaspace/tree/main/fonts/otf</a></p>
<p>拖到 <code>C:\Windows\Fonts</code> 中，点击安装</p>
<p>下载好后，如果是 VSCode 文件，可以在设置中找到 <code>font-family</code>，改为：<code>'Monaspace Radon', monospace</code></p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/15-113023-6WKrCV-07c1c29dcdcf4da1a0c0ba9e1fb643f2.png" alt="" class="img_ev3q"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 如何利用 AI 做乘法，制作一款龙年贺卡小程序]]></title>
            <link>https://wukaipeng.com/blog/ai-as-multiplication</link>
            <guid>https://wukaipeng.com/blog/ai-as-multiplication</guid>
            <pubDate>Sun, 04 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[2022 年底 AIGC 的出现，让 2023 年成为通用人工智能元年。]]></description>
            <content:encoded><![CDATA[<p>2022 年底 AIGC 的出现，让 2023 年成为<strong>通用人工智能元年</strong>。</p>
<p>这是最好的时代，利用 AI，之前仅能存在幻想中的事物落地成现实。</p>
<p>只需要寥寥几句话，就可以描绘一张斑斓的画，真实而又丰富的画。</p>
<p>目前 AI 生图的大模型不多，大名鼎鼎的有 Midjourney，不过它<strong>闭源，并且国内用户使用不方便</strong>。</p>
<p><strong>Stable Diffusion</strong>，一款<strong>免费开源，而又强大的 AI 生图模型</strong>，正在成为 AI 绘画的宠儿。</p>
<p>比如这张庆祝新年的图片，正是出自 Stable Diffusion 之手：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113744-b06nB3-a0f200afc67f4519a1688e5553b06d02.webp" alt="" class="img_ev3q"></p>
<p>Stable Diffusion 支持<strong>文生图</strong>（通过提示词文字生成图片）、<strong>图生图</strong>（通过垫图+提示词文字生成图片）、<strong>文生视频</strong>、<strong>视频生成视频</strong>。</p>
<p>如下是一个图生图的例子，通过一张手绘的垫图（左侧），生成一张真实的苹果图片（右侧）：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113800-7lkToh-c02282039963420ca629af80274b163b.png" alt="" class="img_ev3q"></p>
<p>还有比如前阵子抖音比较火的 AI 变换视频：</p>
<iframe width="720" height="405" frameborder="0" src="https://www.ixigua.com/iframe/7331579249955963442?autoplay=0" referrerpolicy="unsafe-url"></iframe>
<p>为了探索 Stable Diffusion 想象力的边际，在临近甲辰龙年之际，我们决定开发一款龙年贺卡小程序，效果图如下：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113807-IdzR4C-5ab4031e920a4f6b88ba0d1bd440a9fb.png" alt="" class="img_ev3q">
我们选择了一个叫做 Comfy UI 的开源框架，基于工作流的交互，让 Stable Diffusion 的编排和出图更加方便和快捷：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113814-DN9Ljh-f87bf16543334912bf188ff443ba148e.png" alt="" class="img_ev3q"></p>
<p>Comfy UI 官方介绍称之为：“<strong>最强大的 Stable Diffusion 的图形化操作界面</strong>”</p>
<p>👉 官网链接： <a href="https://github.com/comfyanonymous/ComfyUI" target="_blank" rel="noopener noreferrer">https://github.com/comfyanonymous/ComfyUI</a></p>
<p>虽然有 Comfy UI 加持，但在一开始，就碰壁了，<strong>无法生成文字</strong>，如下：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113832-3zUTJj-04-113819-eeqPwz-68510d5092d846f48bf6d74126a66951.jpeg" alt="" class="img_ev3q"></p>
<p>左边这张「龙年大吉」是我们输入给 Stable Diffusion 垫图，右边是图片是基于垫图和提示词生成的图片。</p>
<p>可以看到，文字根本是混乱的。</p>
<p>这个痛点强如 Midjourney 也无法解决，这是由于<strong>文字的特殊性</strong>。</p>
<p>就说汉字，书体有篆书、隶书、楷书、行书、草书等，排列有从左往右、从右往左、从上往下等。</p>
<p>另外一种解决思路是，我们把文字也作为垫图之一，比如这张「龙年大吉」的图片：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113932-k0slDI-068b35eb5125425a964d5117a5bbf862.png" alt="" class="img_ev3q"></p>
<p>生成结果：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113936-8aB6Kq-644cdee4e0294d9b9419a1ddd4e1fd4a.png" alt="" class="img_ev3q"></p>
<p>这个方案似乎能走得通，但是很快又遇到麻烦，对于稍微复杂的字，生成效果就不行了，比如「甲辰臻祥」：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113942-fqj6OI-930ff501491649a2b5e690f7adb0ae56.png" alt="" class="img_ev3q"></p>
<p>为了解决这个问题，加上时间的有限性，只能在产品层面做出改动，贺卡图片由模板 + 主体图片组成，模板提前预制，主体图片则由 AI 生成，不包含文字：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113946-u8tHhU-176624fa71034d168b277f051f28c04e.jpeg" alt="" class="img_ev3q"></p>
<p>另外一个问题，就是涉及到人物，像手部、面部比较复杂的内容时，生成效果较差，如下小女孩的手部：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113951-gykFCB-82a2b592edb547bba6bbd3afe3852755.png" alt="" class="img_ev3q">
这个解决方案较多：</p>
<ol>
<li>可以选择合适的 Stable Diffusion 模型</li>
<li>利用插件做手部、面部的修正</li>
<li>添加负面提示词（Negative Prompt）</li>
</ol>
<p>有了如上的探索，我们终于开始我们的乘法之路。</p>
<p>首先是选定关于新年的元素（Element），比如龙、财神爷、灯笼、白鹤等元素。</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-113955-JpjovL-98c61e20a7aa4ae195b6a019a3c974b8.png" alt="" class="img_ev3q"></p>
<p>其次是风格（Style），我们探索了十多种 Stable Diffusion 生成的风格：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-114002-vYR82T-7037e66c97fb4561b5f4773b51d6b0dc.png" alt="" class="img_ev3q"></p>
<p>Stable Diffusion 稳如老狗，各种风格都驾驭得住。</p>
<p>通过 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mi>l</mi><mi>e</mi><mi>m</mi><mi>e</mi><mi>n</mi><mi>t</mi><mo>×</mo><mi>S</mi><mi>t</mi><mi>y</mi><mi>l</mi><mi>e</mi></mrow><annotation encoding="application/x-tex">Element \times Style</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em"></span><span class="mord mathnormal" style="margin-right:0.01968em">El</span><span class="mord mathnormal">e</span><span class="mord mathnormal">m</span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal">St</span><span class="mord mathnormal" style="margin-right:0.03588em">y</span><span class="mord mathnormal" style="margin-right:0.01968em">l</span><span class="mord mathnormal">e</span></span></span></span>，我们为每一个元素生成对应的风格图片。</p>
<p><strong>乘法的力量是不言而喻的</strong>，在几周时间，我们为了贺卡小程序生成了上万张图片。</p>
<p>AIGC 之前，这绝对实现不了，能做的，只是加法，并且还需要专业的设计师。</p>
<p>假设一个设计师 5 分钟出一张图，一万张图片，不眠不休，至少要 34 天：</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mn>10000</mn><mo>×</mo><mn>5</mn><mo>÷</mo><mn>60</mn><mo>÷</mo><mn>24</mn><mo>=</mo><mn>34.7222222222</mn></mrow><annotation encoding="application/x-tex">10000 \times 5 \div 60 \div 24 = 34.7222222222</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">10000</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">5</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">60</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">÷</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">24</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">34.7222222222</span></span></span></span></span>
<p><strong>这种人力成本、时间成本，一下子就被 AI 打下来了</strong>。</p>
<p>目前 AI 还处于早期阶段，相信随着时间推移，AI 能力会更强，应用的场景会更广，拭目以待。</p>
<p>目前小程序也以及正式上线，名字很有年味，叫做「<strong>画年</strong>」</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-114006-B8hT7A-d3518964160a4d19bce0b5085c1484f4.png" alt="" class="img_ev3q"></p>
<p>操作简单，选择一个钟意的模板，一键生成：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-114013-sktwSW-18483245df8a49ec861bc0f21a4df603.jpeg" alt="" class="img_ev3q"></p>
<p>如果不满意，可以调整配图 or 祝福语：</p>
<p><img decoding="async" loading="lazy" src="https://img.wukaipeng.com/2024/02/04-114019-peTaMO-b0f0494aa31d4a7e90dae33adb0456a8.jpeg" alt="" class="img_ev3q"></p>
<p>如果你有拜年贺卡需求，不妨体验一下这款小程序，目前免费使用。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ ChatGPT 创始人 Sam 有效提高效率的秘诀]]></title>
            <link>https://wukaipeng.com/blog/altman</link>
            <guid>https://wukaipeng.com/blog/altman</guid>
            <pubDate>Sun, 24 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[这是一篇 ChatGPT 创始人山姆·奥特曼（Sam Altman）在 2018 年写的一篇博客，关于如何提升效率的有效建议。]]></description>
            <content:encoded><![CDATA[<p>这是一篇 ChatGPT 创始人山姆·奥特曼（Sam Altman）在 2018 年写的一篇<a href="https://blog.samaltman.com/productivity" target="_blank" rel="noopener noreferrer">博客</a>，关于如何提升效率的有效建议。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="你在做什么">你在做什么？<a href="https://wukaipeng.com/blog/altman#%E4%BD%A0%E5%9C%A8%E5%81%9A%E4%BB%80%E4%B9%88" class="hash-link" aria-label="你在做什么？的直接链接" title="你在做什么？的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="长期主义的本质是复利公式">长期主义的本质是复利公式<a href="https://wukaipeng.com/blog/altman#%E9%95%BF%E6%9C%9F%E4%B8%BB%E4%B9%89%E7%9A%84%E6%9C%AC%E8%B4%A8%E6%98%AF%E5%A4%8D%E5%88%A9%E5%85%AC%E5%BC%8F" class="hash-link" aria-label="长期主义的本质是复利公式的直接链接" title="长期主义的本质是复利公式的直接链接">​</a></h3>
<p>复利是一个金融术语，但它也适合在个人的职业上。如果你每天都在产出，<strong>那么 50 年后你的复利获益将会是巨大的</strong>。提高自己的效率是一件非常重要的事情，从原来每天的 1% 提升到 10%，复利获益会变得更大。</p>
<blockquote>
<p>复利公式见 👉 <a href="https://wukaipeng.com/read/mind/weibo/u/2166767661/01" target="_blank" rel="noopener noreferrer">长期主义的本质是复利公式</a></p>
</blockquote>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="提高效率的核心做你自己喜欢的事情">提高效率的核心：做你自己喜欢的事情<a href="https://wukaipeng.com/blog/altman#%E6%8F%90%E9%AB%98%E6%95%88%E7%8E%87%E7%9A%84%E6%A0%B8%E5%BF%83%E5%81%9A%E4%BD%A0%E8%87%AA%E5%B7%B1%E5%96%9C%E6%AC%A2%E7%9A%84%E4%BA%8B%E6%83%85" class="hash-link" aria-label="提高效率的核心：做你自己喜欢的事情的直接链接" title="提高效率的核心：做你自己喜欢的事情的直接链接">​</a></h3>
<p><strong>在错误的方向上前进就是在倒退</strong>，花点时间去深入思考你自己喜欢做的事情，只有自己热爱的事情，才能真正地提高你的生产效率。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="把自己不喜欢的事情委托给喜欢的人">把自己不喜欢的事情委托给喜欢的人<a href="https://wukaipeng.com/blog/altman#%E6%8A%8A%E8%87%AA%E5%B7%B1%E4%B8%8D%E5%96%9C%E6%AC%A2%E7%9A%84%E4%BA%8B%E6%83%85%E5%A7%94%E6%89%98%E7%BB%99%E5%96%9C%E6%AC%A2%E7%9A%84%E4%BA%BA" class="hash-link" aria-label="把自己不喜欢的事情委托给喜欢的人的直接链接" title="把自己不喜欢的事情委托给喜欢的人的直接链接">​</a></h3>
<p>做自己喜欢的事情才能让自己效率倍增，但面对自己<strong>不喜欢的事情，那么尽量不要去做它</strong>，比如采取委托、避免，或者其他方式。委托给别人的时候要注意，要委托给那些喜欢做这件事情的人，因为从“做你喜欢的事情可以提高效率”这一原则来说，应该也要让被委托者高效地完成这件事情。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="坚持自己的坚持">坚持自己的坚持<a href="https://wukaipeng.com/blog/altman#%E5%9D%9A%E6%8C%81%E8%87%AA%E5%B7%B1%E7%9A%84%E5%9D%9A%E6%8C%81" class="hash-link" aria-label="坚持自己的坚持的直接链接" title="坚持自己的坚持的直接链接">​</a></h3>
<p>这些年能够留给我深刻印象的是那些有强烈信念的人，这种人在大众中很罕见。<strong>如果你发现你总是附和别人，那可不是一件好事</strong>。坚持自己的坚持，有时候可能会出错，但当其他人都在迷茫的时候，你却做了正确而重要的事情，那么你将会变得更加勇敢非凡。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="避免和消耗你的人在一起">避免和消耗你的人在一起<a href="https://wukaipeng.com/blog/altman#%E9%81%BF%E5%85%8D%E5%92%8C%E6%B6%88%E8%80%97%E4%BD%A0%E7%9A%84%E4%BA%BA%E5%9C%A8%E4%B8%80%E8%B5%B7" class="hash-link" aria-label="避免和消耗你的人在一起的直接链接" title="避免和消耗你的人在一起的直接链接">​</a></h3>
<p>要完成一件伟大的事情，应该努力和<strong>那些聪明、积极、高效和开朗的人在一起</strong>，他们不会随意贬低你的理想和抱负，并且他们可以推动你前进，也能给你带来某些启发。在你能力范围之内，避免和让你陷入精神内耗的人一起，否则代价将会是巨大的。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="找对问题并解决它">找对问题并解决它<a href="https://wukaipeng.com/blog/altman#%E6%89%BE%E5%AF%B9%E9%97%AE%E9%A2%98%E5%B9%B6%E8%A7%A3%E5%86%B3%E5%AE%83" class="hash-link" aria-label="找对问题并解决它的直接链接" title="找对问题并解决它的直接链接">​</a></h3>
<p>我们基本上都身处红海行业，竞争激烈，我们要做的就是聪明而勤奋地工作，<strong>找到正确的问题，并正确地去解决它</strong>，这方面没有太多的捷径。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="优先级">优先级<a href="https://wukaipeng.com/blog/altman#%E4%BC%98%E5%85%88%E7%BA%A7" class="hash-link" aria-label="优先级的直接链接" title="优先级的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="三个基本原则">三个基本原则<a href="https://wukaipeng.com/blog/altman#%E4%B8%89%E4%B8%AA%E5%9F%BA%E6%9C%AC%E5%8E%9F%E5%88%99" class="hash-link" aria-label="三个基本原则的直接链接" title="三个基本原则的直接链接">​</a></h3>
<ol>
<li><strong>完成重要的事情</strong></li>
<li><strong>别把时间浪费在 SB 事情上</strong></li>
<li>做大量的<strong>清单</strong></li>
</ol>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="强烈推荐使用清单">强烈推荐使用清单<a href="https://wukaipeng.com/blog/altman#%E5%BC%BA%E7%83%88%E6%8E%A8%E8%8D%90%E4%BD%BF%E7%94%A8%E6%B8%85%E5%8D%95" class="hash-link" aria-label="强烈推荐使用清单的直接链接" title="强烈推荐使用清单的直接链接">​</a></h3>
<p>清单写下每年、每月和每天的任务，<strong>清单可以让你聚焦当下的任务</strong>，并且能够让你处理多个任务（因为你的大脑释放出来，不需要记住那么多的任务）。</p>
<p>可以把清单写在纸上，方便添加和移除，开会的时候进行记录也不会冒犯别人。</p>
<p>经常回顾清单，及时修改或者删除任务，不做分类和排序这样耗费时间的事情，重要的事情就打个“⭐️”。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="优先做产生势头的事情">优先做产生势头的事情<a href="https://wukaipeng.com/blog/altman#%E4%BC%98%E5%85%88%E5%81%9A%E4%BA%A7%E7%94%9F%E5%8A%BF%E5%A4%B4%E7%9A%84%E4%BA%8B%E6%83%85" class="hash-link" aria-label="优先做产生势头的事情的直接链接" title="优先做产生势头的事情的直接链接">​</a></h3>
<p>优先做那些可以产生“势头”的事情，<strong>你做完一件，产生成就感</strong>，继续去做下一件事情</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="学会冷酷无情">学会冷酷无情<a href="https://wukaipeng.com/blog/altman#%E5%AD%A6%E4%BC%9A%E5%86%B7%E9%85%B7%E6%97%A0%E6%83%85" class="hash-link" aria-label="学会冷酷无情的直接链接" title="学会冷酷无情的直接链接">​</a></h3>
<p>“为了要完成我最重要的项目，<strong>我会变得坚韧残酷</strong>——我发现只要我真心想要做成某件事，并且付出足够的努力，通常都能实现。”</p>
<blockquote>
<p>I am relentless about getting my most important projects done—I’ve found that if I really want something to happen and I push hard enough, it usually happens.</p>
</blockquote>
<p>“我尝试变得残酷，<strong>对于无关紧要的事情说不</strong>，对于非关键任务会以最快速的方式实现。我可能做得过头了—比如，我几乎可以确定在回复邮件时，我的简洁到了无礼的程度。</p>
<blockquote>
<p>“I try to be ruthless about saying no to stuff, and doing non-critical things in the quickest way possible.  I probably take this too far—for example, I am almost sure I am terse to the point of rudeness when replying to emails.”</p>
</blockquote>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="维护一个开放的社交网络">维护一个开放的社交网络<a href="https://wukaipeng.com/blog/altman#%E7%BB%B4%E6%8A%A4%E4%B8%80%E4%B8%AA%E5%BC%80%E6%94%BE%E7%9A%84%E7%A4%BE%E4%BA%A4%E7%BD%91%E7%BB%9C" class="hash-link" aria-label="维护一个开放的社交网络的直接链接" title="维护一个开放的社交网络的直接链接">​</a></h3>
<p>努力避免会议和研讨会，因为这会浪费大量的时间和精力。但是日程上，也要保持一定的开放性，去接触一些新的人和想法，维护一个开放的社交网络是很重要的。在研讨会上，<strong>虽然 90% 都是浪费时间，但 10% 有价值的东西可以弥补这样的损失</strong>。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="物理方面">物理方面<a href="https://wukaipeng.com/blog/altman#%E7%89%A9%E7%90%86%E6%96%B9%E9%9D%A2" class="hash-link" aria-label="物理方面的直接链接" title="物理方面的直接链接">​</a></h2>
<p>这一方面围绕睡眠、锻炼和营养展开，有些老生常谈，并且 Sam 是一个极度素食主义，这里忽略，感兴趣可以见原文。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="其他方面">其他方面<a href="https://wukaipeng.com/blog/altman#%E5%85%B6%E4%BB%96%E6%96%B9%E9%9D%A2" class="hash-link" aria-label="其他方面的直接链接" title="其他方面的直接链接">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="舒适的办公环境">舒适的办公环境<a href="https://wukaipeng.com/blog/altman#%E8%88%92%E9%80%82%E7%9A%84%E5%8A%9E%E5%85%AC%E7%8E%AF%E5%A2%83" class="hash-link" aria-label="舒适的办公环境的直接链接" title="舒适的办公环境的直接链接">​</a></h3>
<p>办公室<strong>安静不会被人打扰</strong>，拥有自然光和一台漂亮的办公桌，以及很多台 4K 显示屏。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="一些小妙招">一些小妙招<a href="https://wukaipeng.com/blog/altman#%E4%B8%80%E4%BA%9B%E5%B0%8F%E5%A6%99%E6%8B%9B" class="hash-link" aria-label="一些小妙招的直接链接" title="一些小妙招的直接链接">​</a></h3>
<ul>
<li>
<p>写一些自定义的小工具，让它们去干那些烦人、琐碎重复的工作。</p>
</li>
<li>
<p>提高打字速度</p>
</li>
<li>
<p>学习快捷键</p>
</li>
</ul>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="糟糕的大姨妈">糟糕的大姨妈<a href="https://wukaipeng.com/blog/altman#%E7%B3%9F%E7%B3%95%E7%9A%84%E5%A4%A7%E5%A7%A8%E5%A6%88" class="hash-link" aria-label="糟糕的大姨妈的直接链接" title="糟糕的大姨妈的直接链接">​</a></h3>
<p>像很多人一样，<strong>偶尔会有一两周丧失斗志，对任何事情都提不起劲</strong>。</p>
<p>暂时没有什么好的解决方法，但是相信“大姨妈”总是会过去，过去之后又是精神抖擞，继续奋斗。</p>
<p>另外，在心情很差的时候，避免和其他人接触，这是一个良心建议。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="重视爱人和家庭">重视爱人和家庭<a href="https://wukaipeng.com/blog/altman#%E9%87%8D%E8%A7%86%E7%88%B1%E4%BA%BA%E5%92%8C%E5%AE%B6%E5%BA%AD" class="hash-link" aria-label="重视爱人和家庭的直接链接" title="重视爱人和家庭的直接链接">​</a></h3>
<p>不要为了效率去忽略你的爱人和家庭，<strong>你提高了效率，却失去了快乐</strong>。</p>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="最后">最后<a href="https://wukaipeng.com/blog/altman#%E6%9C%80%E5%90%8E" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2>
<p>Sam 的提高效率的观点值得参考，同时他也强调了：<strong>不要掉入提高效率陷阱中，提高效率不是目的，我们的目的是为了快速、质量地完成任务</strong>。</p>]]></content:encoded>
        </item>
    </channel>
</rss>