<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hyoban</title><description>Do what you like</description><link>https://hyoban.cc</link><item><title>我为什么要远程工作</title><link>https://hyoban.cc/why-remote</link><guid>why-remote</guid><pubDate>Tue, 10 Mar 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你去问 AI 远程工作的利与弊，你会得到类似如下的回答：&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;远程工作的利 (Pros)&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;远程工作的弊 (Cons)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;极高的灵活性：&lt;/strong&gt; 自由安排时间与节奏&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;工作生活模糊：&lt;/strong&gt; 居家办公难以下班&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;省时省钱：&lt;/strong&gt; 无需通勤，降低生活开支&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;孤独感增加：&lt;/strong&gt; 缺乏面对面的人际互动&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;专注度高：&lt;/strong&gt; 减少办公室琐事干扰&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;沟通成本高：&lt;/strong&gt; 信息传递滞后或有偏差&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;地域自由：&lt;/strong&gt; 可跨城市甚至跨国工作&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;晋升受限：&lt;/strong&gt; 远离办公室核心，易被边缘化&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;招聘优势：&lt;/strong&gt; 企业可招揽全球顶级人才&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;协作障碍：&lt;/strong&gt; 难以进行即时的头脑风暴&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;这些大体上都是正确的，我这里就不再赘述了。
我想从我自己出发，来聊聊我为什么需要远程工作。&lt;/p&gt;
&lt;h2 id=&quot;我无法总是在固定时间段保持高效&quot;&gt;我无法总是在固定时间段保持高效&lt;/h2&gt;
&lt;p&gt;我不是个总是能在固定时间段保持产出的人，这和从几点到几点工作/是否加班无关。
有些时候受限于没有好的想法/思路，或者受&lt;a href=&quot;./my-ocd&quot;&gt;强迫症&lt;/a&gt;影响，我无法得到令人满意的产出。
有些时候我遇到难缠的问题/令人激动的想法，可以一直写到深夜，直到事情被解决。&lt;/p&gt;
&lt;p&gt;当然，这不代表我无法胜任工作/我的同事在工作时间会找不到我/我会用这个借口来偷懒。
通常来说，我对于工作时间的投入是会高于每周 40 小时的。
只是我希望可以动态的分配自己的时间，用最高效的状态来工作。&lt;/p&gt;
&lt;p&gt;此外，我也不想把自己珍贵的时间浪费在通勤上。
少数线下的时间我也是尽可能的住离公司近的地方。&lt;/p&gt;
&lt;h2 id=&quot;我需要独立的空间&quot;&gt;我需要独立的空间&lt;/h2&gt;
&lt;p&gt;尽管我的同事们都非常 nice，但是在固定的工位上工作，让我感觉到有些压抑。
办公室工位的问题是它通常空间不足，加上需要考虑身边的人的存在（尽管实际并没有人在意），我无法完全保持放松的状态。&lt;/p&gt;
&lt;p&gt;我写代码的时候老喜欢放一些已经看过的电视剧（最近是《乡村爱情》）作为背景音，这样可以帮助我集中注意力。
但是在办公室里，我就没法这么做，因为戴耳机很不舒服。
我还喜欢在思考的时候来回走动，比比画画，这在办公室太奇怪了。&lt;/p&gt;
&lt;h2 id=&quot;维护状态很麻烦&quot;&gt;维护“状态”很麻烦&lt;/h2&gt;
&lt;p&gt;众所周知，远程的一大优势是可以减少很大一笔通勤成本和经济开销。
但对于我来说更大的优势是我可以减少需要维护的状态。&lt;/p&gt;
&lt;p&gt;强迫症让我希望尽可能减少需要维护的状态，如果一个东西可以让我随意重置/恢复/丢掉，我就会觉得很舒服。
我几乎从来不记笔记，但是生活中需要维护的状态还是让我破例。
我有一个&lt;a href=&quot;/use&quot;&gt;列表&lt;/a&gt;记录我全部用的东西，和它们的状态。&lt;/p&gt;
&lt;p&gt;租房子这件事对于我来说太麻烦，我需要往前提前规划好几个月到一年的时间。
然后我需要和房东/中介打交道，交押金，搬家，维护房子，处理各种琐事。
去年买车这件事虽然总体上让我觉得很满意，但是增加的状态让我一度想要卖掉它。&lt;/p&gt;
&lt;p&gt;当然，这可能和我没钱关系也不小。
如果我能完全不在意租房/买车带来的开销，或许我就不会想要维护这些状态了。
但是现在我还是会在记账的时候翻上好几十页来对上某一笔几块钱的开销，所以我更倾向于这是一个强迫症的问题。&lt;/p&gt;
&lt;h2 id=&quot;我失去了什么&quot;&gt;我失去了什么？&lt;/h2&gt;
&lt;p&gt;远程工作当然有它的弊端，我也失去了一些东西。
工作和生活界限模糊对我来说并不算是什么问题，我本来就很喜欢我的工作，工作就是我生活的一部分。&lt;/p&gt;
&lt;p&gt;我失去了和同事们随意开启一段对话的机会。
对于感兴趣的话题，我本质上算是一个话唠/吐槽怪。
但是远程我不会好好的突然用文字和同事交流一个小时的强迫症病情（在办公室会）。
没办法让大家更多的了解我让我觉得十分可惜（但相信大家还是能从我的文字感受到是个活生生的人）。&lt;/p&gt;
&lt;p&gt;所以，请随意骚扰我（语音都可以），我很乐于放下手上的事情来和你聊天。&lt;/p&gt;
&lt;h2 id=&quot;理想的工作状态&quot;&gt;理想的工作状态？&lt;/h2&gt;
&lt;p&gt;要是我本来就住在我工作的地方的话会很好，我就可以很方便混合办公了。
每周四去办公室疯狂星期四（不是）。
但是我住的地方离公司还是有三四百公里。
所以我倾向于完全远程，隔段时间/特殊时间节点的时候去办公室待一段时间。
这可能对我来来说是更好的工作状态。&lt;/p&gt;</content:encoded></item><item><title>2025 年终总结</title><link>https://hyoban.cc/review-2025</link><guid>review-2025</guid><description>没挣到钱的一年</description><pubDate>Fri, 26 Dec 2025 08:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;工作和开源&quot;&gt;工作和开源&lt;/h2&gt;
&lt;p&gt;对我来说，2025 年最聊不开的话题就是 &lt;a href=&quot;https://folo.is&quot;&gt;Folo&lt;/a&gt;。
这一年来和 DIY、十一、白水等同事们一起工作是我最珍贵的经历。
我有时候觉得自己太菜，经常搞砸事情，很多方面做的也不够好，但是他们对我十分包容和支持。
能把自己最热爱的事情变成工作，这种感觉真的很棒。
我爱自由、爱开源、爱 Folo、爱这些朋友们。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/4EeCnXMdc1gItmJ.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;故事的最后让我感到难过，我总是想如果过去的自己能做得更好更努力一些，或许结果会不一样。
回过头来看，自己今年的表现确实不够好，GitHub 上的绿点也比去年少。&lt;/p&gt;
&lt;p&gt;找工作那几天得到了很多朋友的帮助和支持，也收获了很多厉害前辈们的认可，这里也是十分感谢大家。
年底的时候我找到了一份新工作，在 &lt;a href=&quot;https://dify.ai&quot;&gt;Dify&lt;/a&gt; 做开源，还是远程。
新的领导和同事也很关照我，给了我很多的自由度和支持。
好像都一样，好像哪里也有些不一样。
哦对，入职刚一周，就搞了个 bug 🥲。&lt;/p&gt;
&lt;p&gt;今年大多数开源贡献都是从 Folo 出发做的一点小打小闹，比如维护了一点 RSSHub。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/hyoban_cc/status/1997942616428851400&quot;&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/yasoj2mFnYWNuOd.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;希望明年能够让自己变得更强一点，多贡献一些自己感兴趣的东西，最好能打磨个自己的产品/开源项目收获大家的认可。
也希望自己能多挣点钱，可以不用太为生计发愁。&lt;/p&gt;
&lt;h2 id=&quot;旅行和社交&quot;&gt;旅行和社交&lt;/h2&gt;
&lt;p&gt;今年最大的一笔支出就是买了辆车，经常让我觉得花了太多不该花的钱。
但现在想想还是觉得不后悔，我很享受在路上在车里的感觉。
（甚至在车里也睡过了很多个夜晚）&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/hyoban_cc/status/1957277366138679452&quot;&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/yET5seQGdJZa3Fb.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;买车之后开车去了很多地方。
最长的一段旅程就是开车从安徽去了川西，和同学一起自驾到了雪山边。
车子保险杠第一次刮蹭之后难过得不行，底盘低导致的刮蹭也让我趴着地上看了好久。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/V41BqyJYcSNzG7v.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;找到新工作之后来到了苏州，想着到年前这段线下的时间能多和网友们见见面，交交朋友。
明年也想多出去走走，再扩大一点活动范围，争取可以出次国。
今年还是有太多时间选择窝在老家里了。&lt;/p&gt;
&lt;h2 id=&quot;赛博生活&quot;&gt;赛博生活&lt;/h2&gt;
&lt;p&gt;Dify 在 AI 产品上的报销额度十分慷慨。
我之前主要用的还是开源免费的 Copilot，觉得什么模型都能用，也挺好的，就是到了月底用量就会不够。
现在是 Claude Code 和 VSCode 混着用。
Claude 有一个浏览器插件可以用，这对我这个 Chrome 党很友好，不必用 Atlas。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/xz89KtUXmnTA451.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Claude Code 的话，我不是很习惯在 VSCode 的终端里来使用，就选了 Ghostty 配合着用。
自己一边感叹着 AI 工具的进步和解放的人类生产力，一边又日常被 AI 乱写的代码气得不行。&lt;/p&gt;
&lt;p&gt;Folo 依然是我最喜欢的信息浏览工具，但我也会配合本地的 RSS 阅读器 &lt;a href=&quot;https://netnewswire.com&quot;&gt;NetNewsWire&lt;/a&gt; 使用。
我把 GitHub 订阅、虎扑篮球新闻这种刷新的比较多比较快的源移动到了本地，在 APP 内来刷新。
放弃了一些反爬变严重的源（比如 b 站），回归到官方的订阅方式。&lt;/p&gt;
&lt;p&gt;尝试了离开 Raycast，收窄了我的使用场景。
就用默认的 Spotlight 配合 &lt;a href=&quot;https://sindresorhus.com/supercharge&quot;&gt;Supercharge&lt;/a&gt; 来用。
这是我今年最满意的小工具，治好了我对与 macOS 很多的强迫症。
macOS 内置的剪切板助手勉强也能用。&lt;/p&gt;
&lt;p&gt;今年买了 Mac mini 又卖了，因为自己还是只需要一台便携的笔记本电脑就够了。
自己的 m1 pro 也陪了我快三年，日均成本降到了不到十块钱，简直超值。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/WsnJ9UV2y8Bhlgc.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;买了 PS5 并开了会员但是逐渐吃灰，这两个月在苏州也没带过来。
NAS 倒是一直勤勤恳恳的工作着。
我很喜欢当个电子仓鼠，把喜欢看的电视、电影、动漫都存在里面。
在外面看起来也很方便，手机和车机都能看。&lt;/p&gt;
&lt;h2 id=&quot;情感和家人&quot;&gt;情感和家人&lt;/h2&gt;
&lt;p&gt;今年也是没有对象的一年。
日常因为这个事情被家里人念叨甚至是责备。
但我也没什么办法，一个是我也很难遇到合适的人，另一个是我没那么想谈恋爱了，总觉得会很麻烦，自己一个人这么该着也挺好的。
我只有一个人独处很久的情况下才会觉得孤独，平时有自己忙的事情或者和朋友去玩的时候都挺好的。&lt;/p&gt;
&lt;p&gt;来年也是希望和身边的人都能好好的，不会有什么矛盾，父母都健健康康的。&lt;/p&gt;</content:encoded></item><item><title>Folo 中的状态管理 - 数据库篇</title><link>https://hyoban.cc/folo-database</link><guid>folo-database</guid><pubDate>Tue, 17 Jun 2025 10:50:53 GMT</pubDate><content:encoded>&lt;p&gt;最近将 &lt;a href=&quot;https://follow.is&quot;&gt;Folo&lt;/a&gt; 桌面端和移动端中的状态管理合并到了同一的模块中，就想着记录一下相关的设计和踩坑经验。
（很多是从 &lt;a href=&quot;https://innei.in&quot;&gt;Innei&lt;/a&gt; 的实践中总结出来的，学习到了很多）&lt;/p&gt;
&lt;p&gt;文章大概会有两到三篇，本文中主要介绍数据库的选型和整合。&lt;/p&gt;
&lt;h2 id=&quot;为什么需要数据库&quot;&gt;为什么需要数据库？&lt;/h2&gt;
&lt;p&gt;如果应用较为简单，一般可以直接使用 &lt;a href=&quot;https://tanstack.com/query/latest&quot;&gt;TanStack Query&lt;/a&gt; / &lt;a href=&quot;https://swr.vercel.app&quot;&gt;SWR&lt;/a&gt; 的 Cache 来持久化请求到的数据，以改善应用首屏加载的加载体验。
但是这样的话，一般对于缓存数据的操作会比较麻烦，也可能缺少类型安全。
因此手动控制数据的持久化和预加载，将缓存的管理变得和 TanStack Query/SWR 无关，可能长期看来更好维护。&lt;/p&gt;
&lt;h2 id=&quot;数据库的选型&quot;&gt;数据库的选型&lt;/h2&gt;
&lt;p&gt;因为在移动端使用了 &lt;a href=&quot;https://docs.expo.dev/versions/latest/sdk/sqlite&quot;&gt;Expo SQLite&lt;/a&gt;, 为了保持数据库 schema 一致，避免写两套数据库操作的代码，在桌面端就使用了 SQLite WASM 的方案。
或许也可以看看 &lt;a href=&quot;https://pglite.dev&quot;&gt;PGlite&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在浏览器中运行 SQLite 一般可以使用以下几个库：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sql-js/sql.js&quot;&gt;sql.js&lt;/a&gt; 已知的第一个在 Web 浏览器中直接使用 sqlite3 的程序
&lt;ul&gt;
&lt;li&gt;只支持内存数据库，除了一次性导入导出整个数据库文件外，不支持持久化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rhashimoto/wa-sqlite&quot;&gt;wa-sqlite&lt;/a&gt; 已知的第一个的 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system&quot;&gt;OPFS&lt;/a&gt; 存储实现 sqlite3 数据库，支持很多类型的 VFS&lt;a href=&quot;https://github.com/rhashimoto/wa-sqlite/tree/master/src/examples#vfs-comparison&quot;&gt;（来源）&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sqlite/sqlite-wasm&quot;&gt;SQLite Wasm&lt;/a&gt; sqlite3 WebAssembly 的 javascript 包装
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DallasHoff/sqlocal&quot;&gt;SQLocal&lt;/a&gt;，在 SQLite Wasm 上构建，添加了更高级别的抽象，以便与 SQLite Wasm 交互&lt;a href=&quot;https://github.com/DallasHoff/sqlocal/issues/42#issuecomment-2319543677&quot;&gt;（来源）&lt;/a&gt;。
包括与 Kysely 和 Drizzle ORM 的集成。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关于这三者的比较相关信息，可以查看 &lt;a href=&quot;https://github.com/sqlite/sqlite-wasm/issues/109&quot;&gt;how is this different from the @rhashimoto/wa-sqlite and sql.js?&lt;/a&gt;。
从暴露出来的 API 访问级别来看是，SQLite Wasm &amp;#x3C; wa-sqlite &amp;#x3C; sql.js，SQLite Wasm 最底层。&lt;/p&gt;
&lt;p&gt;最后，SQLocal 是 Folo 桌面端的数据库方案，因为它基于官方的 SQLite Wasm，由 SQLite 核心团队构建，在维护方面的表现应该会更好&lt;a href=&quot;https://github.com/DallasHoff/sqlocal/issues/31#issuecomment-2209431300&quot;&gt;（来源）&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;sqlite-在浏览器中的运行模式&quot;&gt;SQLite 在浏览器中的运行模式&lt;/h2&gt;
&lt;p&gt;SQLite 在浏览器中的运行模式主要有三种，在 &lt;a href=&quot;https://sqlite.org/wasm/doc/trunk/persistence.md&quot;&gt;sqlite3 WebAssembly &amp;#x26; JavaScript Documentation&lt;/a&gt; 中有详细的介绍。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Key-Value VFS (kvvfs)：在主 UI 线程中运行，使用如 localStorage 或 IndexedDB 来持久化数据。
问题是存储空间有限，性能相对较差。&lt;/li&gt;
&lt;li&gt;The Origin-Private FileSystem (OPFS)：在 Worker 中运行，OPFS 对于浏览器的要求相对较高，需要 23 年 3 月之后的浏览器版本。
&lt;ul&gt;
&lt;li&gt;OPFS via sqlite3_vfs：需要 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy&quot;&gt;COOP&lt;/a&gt; 和 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy&quot;&gt;COEP HTTP&lt;/a&gt; 标头以使用 &lt;code&gt;SharedArrayBuffer&lt;/code&gt;，这个要求较高，比较难以满足。
对于图片的加载和外站资源的引入都需要额外的配置。&lt;/li&gt;
&lt;li&gt;OPFS SyncAccessHandle Pool VFS：不需要 COOP 和 COEP HTTP 标头，性能相对更好，但不支持并发连接，文件系统不透明（即并非将数据库保存为一个 sqlite 文件）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些运行模式各有优劣，第一种性能较差，存储空间有限，但对浏览器的要求最低，因此仍有很多应用使用它来存储数据库到 indexedDB。
第二种对于 COOP 和 COEP HTTP 标头的要求较高，难以满足，但第三种的并发支持又比较麻烦有限。
因此，可以在条件允许的情况下，使用第二种，否则回退到第三种。
值得一提的是，PGlite 的文件系统也很相似，在浏览器中同样是 In-memory FS、IndexedDB FS、OPFS AHP FS 三种 &lt;a href=&quot;https://pglite.dev/docs/filesystems&quot;&gt;（来源）&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;前面提到 OPFS SAH 不支持并发，默认情况下，用户打开两个窗口时就会出错。
要如何解决呢？
需要从多个客户端中协商出一个可以执行查询的，然后暂停其他客户端的使用。
PGlite 也有类似的 &lt;a href=&quot;https://pglite.dev/docs/multi-tab-worker&quot;&gt;Multi-tab Worker&lt;/a&gt; 实现。
目前 SQLocal 还没有对 OPFS SAH 的支持，相关的 issue 可以查看 &lt;a href=&quot;https://github.com/DallasHoff/sqlocal/issues/39&quot;&gt;Allow using sqlite’s OPFS_SAH backend&lt;/a&gt;。
我基于作者的实现分支进行了一些探索，实现了基础的支持，但目前测试还未完全通过 &lt;a href=&quot;https://github.com/DallasHoff/sqlocal/pull/76&quot;&gt;（PR）&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;所以 Folo 中会使用哪种运行模式呢？
在本地使用网页代理来开发时，由于跨源运行 worker 的限制，会使用 Key-Value VFS；网页端和桌面端的生产环境中，因为 COOP 和 COEP HTTP 标头的条件无法满足，使用 OPFS SAH VFS；&lt;/p&gt;
&lt;p&gt;不过桌面端 Electron 中，也可以直接开启 &lt;code&gt;SharedArrayBuffer&lt;/code&gt; 的支持，来使用 OPFS via sqlite3_vfs。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;commandLine&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;appendSwitch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;enable-features&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;SharedArrayBuffer&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;值得一提的是，由于 Electron 中使用的协议不同，一般是 &lt;code&gt;file://&lt;/code&gt; 或是自定义的 &lt;code&gt;app://&lt;/code&gt;，因此为了访问安全环境的中才有的 API，需要注册协议。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;// https://github.com/getsentry/sentry-electron/issues/661&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;protocol&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;registerSchemesAsPrivileged&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    scheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;sentry-ipc&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    privileges&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;bypassCSP&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;corsEnabled&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;supportFetchAPI&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;secure&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    scheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    privileges&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      standard&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      bypassCSP&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      supportFetchAPI&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      secure&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 &lt;code&gt;registerSchemesAsPrivileged&lt;/code&gt; 这个 API 最好&lt;a href=&quot;https://www.electronjs.org/docs/latest/api/protocol#protocolregisterschemesasprivilegedcustomschemes&quot;&gt;只被调用一次&lt;/a&gt;，所以如果使用了 sentry 的话，推荐将它的的 &lt;code&gt;registerSchemesAsPrivileged&lt;/code&gt; 调用给 patch 掉，然后在自己的代码中调用。&lt;/p&gt;
&lt;h2 id=&quot;如何为多端复用代码&quot;&gt;如何为多端复用代码？&lt;/h2&gt;
&lt;p&gt;显然桌面端和移动端的 SQLite Client 是不同的，所以在打包的时候需要为不同的平台导入不同的文件。
Folo 的代码使用后缀来区分，比如 &lt;code&gt;db.desktop.ts&lt;/code&gt; 用于桌面端，&lt;code&gt;db.rn.ts&lt;/code&gt; 用于移动端。
Vite 可以通过插件来实现&lt;a href=&quot;https://github.com/RSSNext/Folo/blob/5ddf4b8b18392dfa0b4236fdc0b1392f664ad494/apps/desktop/plugins/vite/specific-import.ts&quot;&gt;（代码）&lt;/a&gt;，Metro 可以通过自定义 &lt;code&gt;resolver.resolveRequest&lt;/code&gt; 来实现&lt;a href=&quot;https://github.com/RSSNext/Folo/blob/5ddf4b8b18392dfa0b4236fdc0b1392f664ad494/apps/mobile/metro.config.js#L28&quot;&gt;（代码）&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;这样就可以给每个平台提供不同的数据库实现了。&lt;code&gt;db.ts&lt;/code&gt; 中定义类型，&lt;code&gt;db.desktop.ts&lt;/code&gt; 和 &lt;code&gt;db.rn.ts&lt;/code&gt; 中实现具体逻辑。
这里由于使用了 Drizzle ORM，所以自然用上了 Drizzle 的数据表类型定义，来给数据库的操作提供一定的类型安全。
至于实际的数据库操作，则和平常写 Drizzle 的代码没有区别。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;// db.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; BaseSQLiteDatabase&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;drizzle-orm/sqlite-core/db&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./schemas&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; DB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; BaseSQLiteDatabase&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    |&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; BaseSQLiteDatabase&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;sync&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; declare const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;sqlite&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;unknown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; declare const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;db&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;DB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; declare&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; initializeDB&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;():&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; declare&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; migrateDB&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;():&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; declare&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; exportDB&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;():&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Blob&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;数据库迁移&quot;&gt;数据库迁移&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Drizzle Kit 有非常好用的 migrate 工具，可以通过 &lt;code&gt;drizzle-kit generate&lt;/code&gt; 命令来生成迁移文件。
它和 Expo SQLite 的整合使用已经有完善的&lt;a href=&quot;https://orm.drizzle.team/docs/connect-expo-sqlite#expo-sqlite-migrations-with-drizzle-kit&quot;&gt;文档&lt;/a&gt;来说明，这里不多赘述。
桌面端的迁移可以基于这套方案。&lt;/li&gt;
&lt;li&gt;因为 migrate 的运行时代码并不依赖 Node，所以也可以在 Web 端来运行&lt;a href=&quot;https://github.com/RSSNext/Folo/blob/5ddf4b8b18392dfa0b4236fdc0b1392f664ad494/packages/internal/database/src/migrator.ts&quot;&gt;（代码）&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;由于生成的 SQL 文件引入语句是直接 import 的，所以为了照顾移动端，这里不使用 Vite 的 &lt;a href=&quot;https://vite.dev/guide/assets#importing-asset-as-string&quot;&gt;&lt;code&gt;?raw&lt;/code&gt;&lt;/a&gt;，而是自定义一个插件，将 SQL 文件文本转成正常的 js 模块导出&lt;a href=&quot;https://github.com/RSSNext/Folo/blob/5ddf4b8b18392dfa0b4236fdc0b1392f664ad494/apps/desktop/configs/vite.render.config.ts#L53-L65&quot;&gt;（代码）&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;最后&quot;&gt;最后&lt;/h2&gt;
&lt;p&gt;这一套下来就能在 Folo 中使用单独的包来维护数据库增删改查相关的逻辑，并且多端的代码实现了复用，减少维护的成本和潜在的实现不一致导致的问题。&lt;/p&gt;
&lt;p&gt;最后留一个小 Tip，Drizzle ORM 的更新操作处理更新值的时候有些麻烦，需要手写每一列名，且没有类型安全，可以创建一个简单的 helper 函数&lt;a href=&quot;https://github.com/drizzle-team/drizzle-orm/issues/1728#issuecomment-2148635569&quot;&gt;（来源）&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;阅读更多&quot;&gt;阅读更多&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.powersync.com/blog/sqlite-persistence-on-the-web&quot;&gt;The Current State of SQLite Persistence on the Web: February 2024 Update&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.com/blog/how-we-sped-up-notion-in-the-browser-with-wasm-sqlite&quot;&gt;How we sped up Notion in the browser with WASM SQLite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>周报一 NAS 真好玩｜恢复记账习惯｜如何收外币</title><link>https://hyoban.cc/weekly-1</link><guid>weekly-1</guid><pubDate>Sat, 04 Jan 2025 14:05:49 GMT</pubDate><content:encoded>&lt;p&gt;买了很早就想买的 NAS，现在只后悔买的晚了。
由于不熟悉 NAS，我问了一些人给我推荐型号。
结果每个人推荐的都不一样，给我纠结了很久。
最后选的是比较基础款的绿联的 dxp4800，想着先用用看，结果发现还挺好的。&lt;/p&gt;
&lt;h2 id=&quot;nas-的使用&quot;&gt;NAS 的使用&lt;/h2&gt;
&lt;h3 id=&quot;影视中心&quot;&gt;影视中心&lt;/h3&gt;
&lt;p&gt;这是我折腾的最多的部分。
最开始折腾的方向是用 BT/PT 来下载，折腾了一大圈下来的感觉就是很麻烦。
找资源很麻烦、下载也很麻烦。
后来就还是用云盘，绿联支持直接从百度云来下载，阿里云盘就需要我用电脑下载好，然后再传到 NAS 上。
因为阿里云盘会员在第三方应用里每个月的高速流量只有 10G，我不想为了这个再加会员的钱。
在 NAS 上可以创建一个单向同步的任务，下载完自动同步到 NAS 上。
注意忽略下载中的文件 &lt;code&gt;.part&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/FOpknm7JVtDlvC8.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;绿联的影视中心初用起来感觉还是挺好的。
视频信息可以很好的识别到。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/mXY4BExsH6NCOLQ.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;折腾 NAS 带来的一个坏结果就是对网络的带宽提出了要求。
我家之前的网络是 100M 的，折腾 NAS 之后直接升级成了 1000M 的。
首付 799，后续每个月 40 元，感觉还行。
现在百度网盘和阿里云盘都能跑满带宽，然后从电脑上传到 NAS 基本也是类似的速度。&lt;/p&gt;
&lt;h3 id=&quot;docker-和虚拟机&quot;&gt;Docker 和虚拟机&lt;/h3&gt;
&lt;p&gt;我的另一个使用场景就是用来跑 Docker。
首先我可以把平时开发 follow 需要用到的容器全搬到 NAS 上，这样笔记本上就不用再跑 OrbStack 了。
其次就是跑各种各样的应用服务，这部分还没有折腾太多。
一个有意思的应用是 &lt;a href=&quot;https://github.com/amtoaer/bili-sync&quot;&gt;bili-sync&lt;/a&gt;，可以很方便的把 B 站的视频同步到 NAS 上。
不过愉快地跑 Docker 之前你需要在自己电脑上拉一个 shell clash 镜像，然后手动上传到 nas 上，再给 Docker 配置代理，不然你可能不能拉到镜像。&lt;/p&gt;
&lt;p&gt;关于虚拟机，我折腾过用它来跑 openwrt 当旁路由，但最后发现还是在各个设备上来控制代理更灵活方便好维护，所以就没再搞了。
如果你感兴趣，这里有两个视频可以抄作业：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/H3yDHG22sRM?si=BSb1447tdvGGiWST&quot;&gt;Nas 安装 iStoreOS 配置软路由教程，你的 Nas 可能就是最好的软路由&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/CzWyPAzU8N4?si=OG_0ptc9ctDLkUxC&quot;&gt;NAS 安装 OpenWrt 实现旁路由科学上网&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要注意的是，这可能会对你的网络环境带来一些影响，比如国内网络访问不了，需要你去找一些解决方案。&lt;/p&gt;
&lt;h2 id=&quot;恢复记账习惯&quot;&gt;恢复记账习惯&lt;/h2&gt;
&lt;p&gt;从 2025 年开始恢复了记账的习惯，然后记的第一笔账就是购买记账软件的的钱。
我用的是 icost，感觉都挺好的。
记账一边能满足我的强迫症思维，一边又会给我带来额外的负担。
因为我会把每一笔变动都希望记录的很清楚，甚至是余额宝每天的那三瓜两枣利息。&lt;/p&gt;
&lt;p&gt;给每笔资金变动做记录和给每个去的地方标点很像，都很能满足我的强迫思维。&lt;/p&gt;
&lt;h2 id=&quot;如何减少收外币的损耗&quot;&gt;如何减少收外币的损耗&lt;/h2&gt;
&lt;p&gt;说起来，我到最近才意识到本地银行转账和国际转账的区别。
前者很快就能到账且不收手续费，后者则需要等至少一天，还需要收一笔手续费，一般是几十刀。
所以当你能有对应币种的本地银行时，就最好是用本地银行转账。
就算你需要换到其他外币，也应该先转到本地银行，再用 wise 换到其它币种。
因为这种情况 wise 入金一般不收钱且汇率也比较好。&lt;/p&gt;
&lt;p&gt;一个题外话，在上次大家都收到清退的消息后，我发现我还能用 OCBC，也没有收到邮件。
但是我已经不敢往里面放很多钱了。&lt;/p&gt;
&lt;h2 id=&quot;结语&quot;&gt;结语&lt;/h2&gt;
&lt;p&gt;这篇周报的结尾放一下我目前的桌面设置。
升级宽带之后，联通的方案给了个路由器，我就把原本的红米 AX5 拿到了房间里。
这样我自己瞎折腾网络的时候就不会影响到家里的人了。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/LVU7oBmOPgD96kT.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>2024 年终总结</title><link>https://hyoban.cc/review-2024</link><guid>review-2024</guid><description>实现梦想的一年，希望未来自己能持续进步。</description><pubDate>Wed, 01 Jan 2025 03:12:15 GMT</pubDate><content:encoded>&lt;h2 id=&quot;毕业&quot;&gt;毕业&lt;/h2&gt;
&lt;p&gt;今年上半年，我终于从待了七年的学校毕业了。
这对我来说是个解脱。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如何评价研究生的时光对我的意义？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一方面来说，我在研究生的阶段变化最大。
找到了自己真正喜欢做的事情，形成了自己的人生观。
学习前端，参与开源，远程兼职，找工作，几乎每个时间段都留下了值得记录的痕迹。
如果你对这些感兴趣，可以阅读这两篇文章。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hyoban.cc/work-one-year&quot;&gt;https://hyoban.cc/work-one-year&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hyoban.cc/24-years&quot;&gt;https://hyoban.cc/24-years&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;另一方面来说，对于很不擅长、不喜欢科研的我，这也是我最容易遇到烦心事的日子。
因为学校和导师的事情很难让我产生兴趣，整体环境的氛围又很难和自由扯得上半毛钱关系。
到最后，三年的时间换回来的是一篇东拼西凑的论文和降低进入社会难度的通行证。
不过还好遇上了一些很好的同学，给我带来了很多的安慰。&lt;/p&gt;
&lt;h2 id=&quot;工作&quot;&gt;工作&lt;/h2&gt;
&lt;p&gt;毕业之后我在美团工作了两个月。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在美团的工作体验如何？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;老实说，美团的工作整体还是很好的。
基本上我每天七八点就可以下班，工作的内容也不会很麻烦，组里的同事和领导人也都很 nice。
如果我没有更理想自由的工作，暂时在这里工作也很好。
但是这毕竟是一份角色更像一个螺丝钉的工作，只能一直在北京租房的生活也不是我想要的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么可以果断地选择离开互联网？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在很多人的视角里，毕业初期就辞去一个互联网公司的“稳定“工作，看起来不是一个稳妥的选择，但我却不需要任何考虑就可以做出这个决定。
因为我一直以来的理想就是能够获得一份远程开源的工作，做自己喜欢的东西，自由的生活。
如我喜欢的一句话所说，Don’t do what you should do, do you want.
（这是电视剧 Silicon Valley 中 Russ Hanneman 说的，他人虽然不靠谱，但这话还是很对的）&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;从九月开始我就加入了 RSS3，三个月后顺利的转正。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我是怎么找到这样一份理想自由的工作的？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;说起来其实很简单，我就是一直做自己喜欢做的事情。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过对我很喜欢、一直在用的 xLog 和 RSSHub 相关项目的贡献让我认识了 DIYgod。&lt;/li&gt;
&lt;li&gt;看到 DIYgod 今年开的 Follow 的新坑，发现很符合我心中 RSS 阅读器的样子，就提前混进去了项目。&lt;/li&gt;
&lt;li&gt;在 Follow 项目中的参与让我收到了邀请，可以正式加入一起全职开发。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;做这些事情的出发点都是我很喜欢，不考虑这些可以给我带来什么。
如果我没有获得在 Follow 的全职工作，我还是会在空余时间自己去参与，这是不给我钱我也愿意做的事情。
从我的经历来看，其实这样你更容易感受到快乐和充实，获得一个自己喜欢的结局。
就和打游戏不去刻意刷任务看攻略追求完美一样。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 RSS3 的工作如何？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;来到 RSS3 之后，我的感觉就是来到了一个可以至少待 10 年的地方，我目前找不到任何缺点，这是最适合我的工作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 Follow 的工作，没有人给你派任务，都是自己按兴趣驱动找事情干，做的产品也是开源的。&lt;/li&gt;
&lt;li&gt;团队氛围十分友好，感觉可以和团队里的任何成员都成为好朋友。&lt;/li&gt;
&lt;li&gt;完全远程+无固定的工作时间，可以很好的兼顾工作和生活。&lt;/li&gt;
&lt;li&gt;薪资待遇不妥协，我可以不用考虑生活的苟且。（
这个最不重要）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;开源&quot;&gt;开源&lt;/h2&gt;
&lt;p&gt;今年开启了每月赞助开源的计划，预算为 20 刀每月。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我赞助了哪些人？
总共花了多少钱？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你可以在我的 &lt;a href=&quot;https://github.com/hyoban?tab=sponsoring&quot;&gt;GitHub&lt;/a&gt;、&lt;a href=&quot;https://opencollective.com/hyoban&quot;&gt;open collective&lt;/a&gt;、&lt;a href=&quot;https://afdian.com/a/hyoban&quot;&gt;爱发电&lt;/a&gt; 主页上看到我赞助过哪些人或项目。
大部分都是我在用的项目、我阅读的博客的作者。&lt;/p&gt;
&lt;p&gt;具体赞助的数额如下，总计约 253.35 usd，平均到每个月约 21.11 usd。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub 168.06 usd&lt;/li&gt;
&lt;li&gt;open collective 47.58 sgd&lt;/li&gt;
&lt;li&gt;爱发电 270 cny&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;我以什么样的方式来赞助？
为什么要每月赞助社区？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;刚开始我选择按月长期赞助某些人再加上 Anthony Fu 的 &lt;a href=&quot;https://antfu.me/posts/sponsorship-forwarding&quot;&gt;赞助转发计划&lt;/a&gt;。
但是后来发现这样我的预算涉及到的开源作者很有限，就选择自己手动一次性赞助，每次赞助 5 刀。&lt;/p&gt;
&lt;p&gt;我每天会用到很多开源作者的项目，但这其中很多人的工作可能被忽视，他们的工作得不到相对应的回报。
我觉得这样长期下去是很不利于开源社区的发展的，所以我想为开源社区尽我的一份力。&lt;/p&gt;
&lt;p&gt;今年我也收到了一些人的赞助，非常感谢 &lt;a href=&quot;https://github.com/cuikaipeng&quot;&gt;cuikaipeng&lt;/a&gt; &lt;a href=&quot;https://afdian.com/a/Hakadao&quot;&gt;Hakadao&lt;/a&gt; &lt;a href=&quot;https://afdian.com/a/emiyaaaaa&quot;&gt;emiyaaaaa&lt;/a&gt; 对我的赞助，我会将这笔钱作为我来年赞助预算的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;今年我成功让自己的一点小项目被别人使用，同时在 Follow 中做出了自己的一点贡献。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有哪些项目想要分享一下？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hyoban/tailwindcss-classname-highlight&quot;&gt;tailwindcss-classname-highlight&lt;/a&gt; 高亮你代码文件中会正确生成到 css 的 classname，今年支持了 tailwind v4 beta。
我也抽离出其中的和 tailwind api 交互的部分，发布了 &lt;a href=&quot;https://github.com/hyoban/tailwind-api-utils&quot;&gt;tailwind-api-utils&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hyoban/eslint-config-hyoban&quot;&gt;eslint-config-hyoban&lt;/a&gt; 一个自认为还不错的 eslint 预设，主要包含 react 和 ts 相关的插件和规则。
如果你在用且有想配置的规则和插件的话，欢迎提 issue 让我来加入。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你可以在我的 &lt;a href=&quot;https://github.com/hyoban&quot;&gt;GitHub 主页&lt;/a&gt; 中查看更多的项目。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目前主要专注于什么开源项目？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为现在的全职开源工作就是做 Follow，所以大部分的时间都放在了和 Follow 相关的开发上。
有时候也会顺着平时开发遇到的需求和问题去给上游提 pr。
比如最近做了些和 auth 相关的功能，就给 &lt;a href=&quot;https://better-auth.com&quot;&gt;better-auth&lt;/a&gt; 修了点小 bug，总体上它算是个很好用的 auth 相关的库，各种集成很丰富。&lt;/p&gt;
&lt;h2 id=&quot;生活&quot;&gt;生活&lt;/h2&gt;
&lt;p&gt;离开了北京之后，我开始了在不同城市旅居的生活，今年去了 青岛、郑州、南京、成都、长沙、深圳、香港、广州、上海。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我是如何旅居、如何平衡工作和生活的？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;今年的整体节奏是每一两周换一个城市，选择的城市多是已经有认识的同学或朋友的地方。
我在周一到周五固定在一个城市不动，找一个稳定的地方工作。
因为当地有同学，所以很大概率能去蹭一下住的地方。
在周末，我做飞机或者火车去到下一个城市，可能会随机逛逛，和朋友一起约个饭。&lt;/p&gt;
&lt;p&gt;这听起来更像是不停换地方工作。
因为我不是个很喜欢到处去打卡游历的人，再加上如果一周的时间被工作和游玩塞满的话，我会没有足够的精力。
所以其实下半年的旅居生活是分成两部分，十月的时候正好避开国庆在家休息，然后结束广州之行后也回到了家里休息。&lt;/p&gt;
&lt;p&gt;明年打算在不同的地方待的时间延长到至少一个月，减少我在旅途上的消耗，也留给我更多的时间去感受这个城市。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有什么值得一提的事情吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;借着在 Follow 工作，和团队成员一起录了一期 &lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/67489d8c0ed328720a0ed3ca&quot;&gt;web worker&lt;/a&gt;。
因为是自己一直喜欢听的节目，所以自己有机会能登上这个节目感觉到兴奋。&lt;/p&gt;
&lt;p&gt;尽管还没有变成一个很主动、外向的人，但是今年也和一些之前在网上有交流的朋友线下见了面，逐渐克服了无法在陌生环境生存的问题，很感谢愿意和我互动玩耍的朋友们（&lt;a href=&quot;https://x.com/situ200l&quot;&gt;situ&lt;/a&gt; &lt;a href=&quot;https://x.com/_cosine_x&quot;&gt;cosine&lt;/a&gt; &lt;a href=&quot;https://x.com/YukiHakarigoto&quot;&gt;雪谋&lt;/a&gt; &lt;a href=&quot;https://x.com/Hakadaooo&quot;&gt;Hakadao&lt;/a&gt; &lt;a href=&quot;https://x.com/0xqiuqiuu&quot;&gt;qiuqiu&lt;/a&gt;）。
也欢迎新的朋友来和我交流，我很乐意于认识新的朋友，有机会我们一起约饭玩耍。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;今年逐渐开始买一些之前没买过的数码装备&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;买了哪些值得一说的东西？
还计划买什么？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;nuphy air 60 v2 第一次用矮轴的键盘，加上它的尺寸和重量，变成了我和 Mac 在外旅行的好帮手&lt;/li&gt;
&lt;li&gt;AirPods 第四代 第一次用无线降噪耳机，因为旅居发现它能在很多场景发挥关键作用。&lt;/li&gt;
&lt;li&gt;ps5 我的第一台游戏主机，我在 2024 才开始玩荒野大镖客 2，欢迎大家给我推荐游戏&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最近还想买一个 nas，但是推荐的五花八门，有点不知道具体买哪个型号。
明年打算换一个新的电脑，现在的 m1pro 有点不够用，主要是内存只有 16g。&lt;/p&gt;
&lt;h2 id=&quot;情感&quot;&gt;情感&lt;/h2&gt;
&lt;p&gt;今年也是继续单身的一年，欢迎大家帮我介绍对象。
不过感觉我找对象会很不容易，因为身高很矮（不到一米七）、长得也不好看。&lt;/p&gt;
&lt;p&gt;之前自己一直都不太希望和之前的朋友因为日常生活没有交集而逐渐变得疏远，现在逐渐接受这个事实。&lt;/p&gt;
&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;
&lt;p&gt;好啦，这就是我的 2024 年终总结。
2024 对我来说是实现了梦想的一年，也是新旅程的起点。
希望在未来的日子里，自己还是可以继续做一个自由有趣的人，继续做自己喜欢的事情，同时为自己的未来的日子做好规划。&lt;/p&gt;
&lt;p&gt;如你所见，这篇总结中没有一张图片，因为我还没有拍值得存储的照片的习惯，明年希望可以养成记录生活的习惯，让下一年的总结多彩一点。&lt;/p&gt;</content:encoded></item><item><title>我的强迫症</title><link>https://hyoban.cc/my-ocd</link><guid>my-ocd</guid><pubDate>Sat, 28 Dec 2024 14:56:24 GMT</pubDate><content:encoded>&lt;p&gt;我有挺严重的强迫症，一旦发作，我几乎是什么也干不了的状态。&lt;/p&gt;
&lt;p&gt;对于刚买的或是对我很重要的电子产品，很容易觉得它的状态已经变成我无法接受的了。
最常见情况的就是看见污垢或者指纹，然后用湿巾和布擦个没完没了。
我几乎从来不用 Mac 的内置键盘，就是因为它很容易沾指纹，我就会需要在一天内多次擦拭。
不过这种一般还好，一般忍不住去擦的频率在一周两次左右，并且因为可以擦干净就还是可以缓解。
另一种比较难缓解的情况是磕碰或者无法去除的痕迹。
之前我的电脑贴过贴纸，那时候我觉得这样可以让我的电脑变得个性化一些。
但是一段时间后我开始不喜欢这种状态，然后就非要把贴纸撕下来。
虽然贴纸没有留下那种清理不掉的胶，但还是留下了贴过贴纸的痕迹。
这时常能让我感到难受，下次换电脑一定不贴任何贴纸了。&lt;/p&gt;
&lt;p&gt;另一个很容易触发我强迫症的场景是我无法使用我最常用的 ID。
hyoban 这个 ID 已经是我为了在各个平台不容易触发 ID 已经被使用的场景而随机组合的一个顺眼 ID 了，但它还是会遇到重复的问题。
为这个平台绞尽脑汁想了一个不重复的 ID 之后，过一段时间再看到这个 ID 还是会感到难受。
有的时候我能够用一些理由说服自己不要再去想这个事情，比如如果再修改 ID 的话，一些地方的链接就会失效不能用。
但更多时候我会去想如何再想一个和原 ID 接近，看起来又很顺我眼的新 ID。
这种场景一旦发生我就会陷入其中无法挣脱，直达触发平台的修改 ID 次数限制。&lt;/p&gt;
&lt;p&gt;其它的会让我感到难受的场景有：反复的用洗手液洗手；没完没了的觉得门没关好或是门没上锁；没完没了的整理东西（从 A 搬到 B，再从 B 搬到 A）；一旦想起来剪指甲就剪的一点不剩，然后又觉得指甲太短而感到难受；安装卸载软件来回不断，还会因为觉得软件无法完全清理干净而不爽（用 Windows 的时候，经常不到一个月就要重装系统）。
这些事情一般并不会给我带来什么有价值的效果，我也在做这些事情的过程感到被折磨。&lt;/p&gt;
&lt;p&gt;多数的场景我能够通过有意识的阻止来从中解脱，比如故意不戴手机壳，让手机比较快的变成一种战损状态，从而让我忽略手机上那些极小的外观问题。
但有的场景我还是无法完全治愈，无法在不同地方使用一个 ID 就是一个例子，我没有办法找到一个“说法”来让自己接受这个事情，又或是选出一个虽然不同但同样能让我接受的 ID。
这种时候我一般就需要强迫自己去做完全不想干的事情来强行切换状态。&lt;/p&gt;
&lt;p&gt;一般来说，如果自己有主要的在做的事情，又或是和其他人在一起的话，就几乎不会出现强迫症的症状。
相反的话，情况就会恶化，本来不会触发强迫症的场景也会让自己感到难受。&lt;/p&gt;
&lt;p&gt;写于被强迫症折磨好几个小时的一个晚上。&lt;/p&gt;</content:encoded></item><item><title>Re: 从零开始的 React Native 之旅</title><link>https://hyoban.cc/react-native-follow</link><guid>react-native-follow</guid><pubDate>Sun, 21 Jul 2024 16:48:51 GMT</pubDate><content:encoded>&lt;h2 id=&quot;为什么要写-rn&quot;&gt;为什么要写 rn&lt;/h2&gt;
&lt;p&gt;一个是我自己还没正儿八经地写过 rn，想试试它的体验怎么样。
加上最近对 Follow 这个 RSS 阅读器很感兴趣，但是它暂时还没移动端，可以作为我边学习边实践的对象。
再有就是最近开始上班了，自己老是没什么动力在下班后写点想写的代码，有个目标更容易让自己专注。&lt;/p&gt;
&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;
&lt;h3 id=&quot;hello-world&quot;&gt;hello world&lt;/h3&gt;
&lt;p&gt;好了，废话不多说，让我们跑起来第一个 app 吧。
不过正所谓“工欲善其事，必先利其器”，我们先准备好环境。&lt;/p&gt;
&lt;p&gt;一般来说你只需要安装好 Xcode 就行了，不过如果你像我一样，最近升级了 macOS beta 的话，就会麻烦一些：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;App Store 里的 Xcode 是不能打开的，和系统版本不匹配。&lt;/li&gt;
&lt;li&gt;下载完的 Xcode beta 没办法直接打开，提示 &lt;code&gt;the plug-in or one of its prerequisite plug-ins may be missing or damaged and may need to be reinstalled.&lt;/code&gt;，需要手动安装 &lt;code&gt;Xcode.app/Contents/Resources/Packages/&lt;/code&gt; 下的安装包。
参见 &lt;a href=&quot;https://forums.developer.apple.com/forums/thread/660860&quot;&gt;https://forums.developer.apple.com/forums/thread/660860&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;命令行中需要 select 到你在用的 beta 版 Xcode，&lt;code&gt;xcode-select -s /Applications/Xcode-beta.app&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后就是需要一个 nice 的脚手架，我不太熟悉 rn 这边的技术栈，看完 &lt;a href=&quot;https://stateofreactnative.com&quot;&gt;State of React Native&lt;/a&gt; 就选择了之前在 Twitter 上看到的 &lt;a href=&quot;https://rn.new&quot;&gt;Create Expo Stack&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DanStepanov/status/1800306385797980320&quot;&gt;https://x.com/DanStepanov/status/1800306385797980320&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;它除了作为一个 expo 项目的脚手架之外，还给你提供了很多主流技术栈的组合选项，这对于我想尽快开始写 app 非常友好。
最终我选择的组合是：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; create-expo-stack@latest&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; follow-app&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --expo-router&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --tabs&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --tamagui&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --pnpm&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --eas&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;处理深色模式&quot;&gt;处理深色模式&lt;/h3&gt;
&lt;p&gt;脚手架默认为只支持浅色模式，强迫症表示不能接受，所以首先处理一下先。
参考这个 &lt;a href=&quot;https://github.com/facebook/react-native/issues/31806&quot;&gt;issue&lt;/a&gt;，我需要修改 expo 的设置为：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;expo&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;userInterfaceStyle&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;automatic&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;ios&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;userInterfaceStyle&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;automatic&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;android&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;userInterfaceStyle&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;automatic&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后你的 &lt;code&gt;useColorScheme&lt;/code&gt; 就能正常获得用户当前选择的主题模式。
不过需要注意的是，修改完这个配置，你需要再执行一次 &lt;code&gt;expo prebuild&lt;/code&gt;，确保 Info.plist 文件里 key 为 &lt;code&gt;UIUserInterfaceStyle&lt;/code&gt; 的值为 &lt;code&gt;Automatic&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&quot;正戏开始&quot;&gt;正戏开始&lt;/h2&gt;
&lt;p&gt;好了，现在我们来写 Follow app 吧！&lt;/p&gt;
&lt;h3 id=&quot;登录账号&quot;&gt;登录账号&lt;/h3&gt;
&lt;p&gt;虽然 expo 文档有很详细的 &lt;a href=&quot;https://docs.expo.dev/guides/authentication&quot;&gt;Authentication 接入文档&lt;/a&gt;，但我们不需要使用它。
Follow 的网页端已经处理好了，我们只需要调用网页端的登录，为 app 注册处理网页登录后会跳转的 scheme 链接就好。&lt;/p&gt;
&lt;p&gt;首先设置好 app 的 scheme，在 app config 里面设置 &lt;code&gt;scheme: &apos;follow&apos;&lt;/code&gt;，然后运行一下 &lt;code&gt;expo prebuild&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;expo-web-browser&lt;/code&gt; 打开 Follow 登录页面：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; WebBrowser&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;openBrowserAsync&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;https://dev.follow.is/login&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后用 &lt;code&gt;expo-linking&lt;/code&gt; 注册 url 的监听事件，在接收到登录网页调起的 url 信息后，解析里面的 token。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;Linking&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ({&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; url&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; hostname&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; queryParams&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Linking&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;hostname&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;auth&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; queryParams&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; queryParams&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    WebBrowser&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;dismissBrowser&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;Platform&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;OS&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;web&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      SecureStore&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;setItemAsync&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;SECURE_AUTH_TOKEN_KEY&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; queryParams&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里还遇到的一个问题是 iPhone 上 Safari 的异步函数里的 &lt;code&gt;window.open&lt;/code&gt; 会无效，需要加上 &lt;code&gt;target=&quot;_top&quot;&lt;/code&gt; 的参数。
参考 &lt;a href=&quot;https://stackoverflow.com/q/20696041/15548365&quot;&gt;https://stackoverflow.com/q/20696041/15548365&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;因为 url 会跳到 auth 这个页面，我们可以加个让它跳到主页的路由 &lt;code&gt;app/auth.tsx&lt;/code&gt;。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; router&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;expo-router&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; Auth&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  router&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;navigate&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OK，这样我们就已经能够获取到用户的认证凭据了。
来试试调个接口看看。&lt;/p&gt;
&lt;h3 id=&quot;获取用户信息&quot;&gt;获取用户信息&lt;/h3&gt;
&lt;p&gt;在 rn 中发起网络请求看起来和 web 没有区别，我们仍然可以使用自己喜欢的库。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useSession&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useSWR&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;URL_TO_FOLLOW_SERVER&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;authToken&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SecureStore&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;getItemAsync&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;SECURE_AUTH_TOKEN_KEY&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      headers&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;        cookie&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;authjs.session-token=&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;authToken&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      credentials&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;omit&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;())&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我暂时做了一点反常的设置，是因为 rn 中基于 cookie 的身份验证存在一些 &lt;a href=&quot;https://reactnative.dev/docs/network#known-issues-with-fetch-and-cookie-based-authentication&quot;&gt;已知的问题&lt;/a&gt;，如果不设置 &lt;code&gt;credentials: &apos;omit&apos;&lt;/code&gt; 的话，就会在第二次请求时设置不正确的 cookie，导致请求失败。
这里是参考 &lt;a href=&quot;https://github.com/facebook/react-native/issues/23185#issuecomment-1148130842&quot;&gt;https://github.com/facebook/react-native/issues/23185#issuecomment-1148130842&lt;/a&gt; 的做法。&lt;/p&gt;
&lt;p&gt;有了数据我们就可以渲染页面，这里先简单写写：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; UserInfo&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; session&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; mutate&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useSession&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;YStack&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; padding&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;        ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;YStack&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;              &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;XStack&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; gap&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; alignItems&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;                  source&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;                    uri&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; session&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;image&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;                    height&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;                    width&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                  }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;                  borderRadius&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;YStack&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; gap&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; color&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;$color12&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; fontSize&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;$8&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; fontWeight&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                    {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; color&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;$color12&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; fontSize&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;$5&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                    {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;session&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;                &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;YStack&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;              &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;XStack&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;            &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;YStack&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;          )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;        :&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; onPress&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;handlePressButtonAsync&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&gt;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;Login&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;          )}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;YStack&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;好了，来看看现在的效果。&lt;/p&gt;
&lt;p&gt;:::div{style=“max-width: 400px”}&lt;/p&gt;
&lt;video&gt;
  &lt;source src=&quot;https://ipfs.crossbell.io/ipfs/QmfLkDiQ74q1V9wXUBkHXCZGNU4dPLSB9dabXurEearWSa&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;啊哦，看起来 Follow 的网页端还需要做点移动端适配，我又可以水 PR 了。&lt;/p&gt;
&lt;h2 id=&quot;主题系统&quot;&gt;主题系统&lt;/h2&gt;
&lt;p&gt;初始化项目的时候我选的 Tamagui，但是当我要开始自定义主题系统的时候，看文档看得我头晕😵‍💫。
加上它大包大揽的风格，让我切换到了 &lt;a href=&quot;https://reactnativeunistyles.vercel.app/start/introduction/&quot;&gt;Unistyles&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;它的主题系统就是普通的对象，我只需要将我十分喜欢的 Radix Color 传递给它就好。
和 Tailwind 的配色不同的是，它为每个颜色都设计了对应的深色，支持深色主题变得十分简单。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;lightTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  colors&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accent&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accentA&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;darkTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  colors&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accentDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accentDarkA&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为要传递的颜色还比较多，容易忘记在对应的深色主题也添加上对应的主题，可以通过类型检查来进行约束。
参考 &lt;a href=&quot;https://www.totaltypescript.com/how-to-test-your-types&quot;&gt;How to test your types&lt;/a&gt; 一文。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Expect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Equal&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;X&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Y&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; X&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Y&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; _ExpectLightAndDarkThemesHaveSameKeys&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Expect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Equal&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  keyof&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; lightTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;colors&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  keyof&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; darkTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;colors&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此外，你可以利用它的运行时来轻松修改主题，那么写像下面这样的动态主题切换就十分简单了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/0xhyoban/status/1815764236494377263&quot;&gt;https://x.com/0xhyoban/status/1815764236494377263&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;UnistylesRuntime&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;updateTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  UnistylesRuntime&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;themeName&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  oldTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;oldTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    colors&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;oldTheme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;colors&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accent&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accentA&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accentDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;accentDarkA&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;local-first&quot;&gt;Local First&lt;/h2&gt;
&lt;p&gt;如果说是写网页的话，不做 Local First 还情有所原。
APP 作为可以跑 SQLite 的环境，没什么理由不能在无网的环境中打开。
目前我的想法是 APP 主要和本地的数据库进行交互，利用网络请求来进行数据的同步。&lt;/p&gt;
&lt;p&gt;关于技术栈的选型，毫不犹豫的就选择了 drizzle，原因有如下几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;目前 Follow 的 server 端也在用，我甚至能 copy 很多表的定义。&lt;/li&gt;
&lt;li&gt;比起 Prisma 这种利用代码生成来做类型的库，我还是更喜欢用 ts 来写表定义，让类型即时刷新。&lt;/li&gt;
&lt;li&gt;Expo &lt;a href=&quot;https://docs.expo.dev/versions/latest/sdk/sqlite/#drizzle-orm&quot;&gt;官方文档&lt;/a&gt; 推荐的和 Expo SQLite 的整合就是 drizzle，&lt;a href=&quot;https://github.com/prisma/react-native-prisma&quot;&gt;Prisma 的集成&lt;/a&gt; 还处在 Early Access 阶段。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expo SQLite 提供了 &lt;code&gt;addDatabaseChangeListener&lt;/code&gt; 的接口，使得我们可以实时获得数据库中最新的数据，drizzle 就提供了 &lt;code&gt;useLiveQuery&lt;/code&gt; 的封装。
不过目前它的 hook 存在没有正确处理 &lt;code&gt;useEffect&lt;/code&gt; 依赖数组的问题：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/0xhyoban/status/1817150515094147279&quot;&gt;https://x.com/0xhyoban/status/1817150515094147279&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;此外，我们还需要对结果进行缓存，否则来会切换页面时会有很多不必要的数据库查询。
所以，我们自己利用 swr 来包装一个 hook。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; AnySQLiteSelect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;drizzle-orm/sqlite-core&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Key&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;swr&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SWRSubscriptionOptions&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;swr/subscription&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQL&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Subquery&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;drizzle-orm&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; getTableConfig&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; getViewConfig&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteTable&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteView&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;drizzle-orm/sqlite-core&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteRelationalQuery&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;drizzle-orm/sqlite-core/query-builders/query&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; addDatabaseChangeListener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;expo-sqlite/next&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; useSWRSubscription&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;swr/subscription&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useQuerySubscription&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  |&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Pick&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;AnySQLiteSelect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  |&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; SQLiteRelationalQuery&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;sync&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  SWRSubKey&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Key&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  query&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  key&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;SWRSubKey&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; subscribe&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;_key&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;SWRSubKey&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;SWRSubscriptionOptions&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Awaited&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteRelationalQuery&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;    // @ts-expect-error&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      ?&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; query&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;table&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;      // @ts-expect-error&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      :&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; AnySQLiteSelect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;table&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Subquery&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQL&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;      next&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;Selecting from subqueries and SQL are not supported in useQuerySubscription&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;      return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    query&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;undefined&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    let &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;listener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;ReturnType&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; addDatabaseChangeListener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteTable&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteView&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; SQLiteTable&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ? &lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;getTableConfig&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;getViewConfig&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;entity&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      let &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;queryTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;NodeJS&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Timeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;undefined&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      listener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; addDatabaseChangeListener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(({&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; tableName&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; tableName&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;          if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;queryTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;            clearTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;queryTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;          queryTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; setTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;            query&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;undefined&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;              .&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;          },&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      listener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;?.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useSWRSubscription&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Awaited&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; SWRSubKey&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    key&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    subscribe&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这里的 queryTimeout ！！！。
由于表变化可能十分频繁，我们需要取消掉之前的查询，否则会影响查询的效率。
Drizzle 还不支持用 AbortSignal 来取消查询，所以用 setTimeout 来处理。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/drizzle-team/drizzle-orm/issues/1602&quot;&gt;https://github.com/drizzle-team/drizzle-orm/issues/1602&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;OK，这样我们只要在请求数据的时候正确地设置 key，就能高效地获取最新的数据了。
配合下拉刷新和定时同步数据，我们的 APP 就能够实现基本的 Local First 了。&lt;/p&gt;
&lt;p&gt;最后一起看看它现在的样子！&lt;/p&gt;
&lt;p&gt;:::div{style=“max-width: 400px”}&lt;/p&gt;
&lt;video&gt;
  &lt;source src=&quot;https://ipfs.crossbell.io/ipfs/QmQcyByJdtfqeD9jb5UjmNPNBURjWhGKxoWkZpA8wxivoW&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h2 id=&quot;分享你写的-ios-app&quot;&gt;分享你写的 iOS App&lt;/h2&gt;
&lt;p&gt;这篇笔记是记录我在分发 App 给到别人测试的过程中踩的坑，希望能让你少踩一次，当然前提你需要有 Apple Developer 的账号。&lt;/p&gt;
&lt;p&gt;参考 Expo 的 &lt;a href=&quot;https://docs.expo.dev/guides/sharing-preview-releases&quot;&gt;Share pre-release versions of your app&lt;/a&gt; 一文，你有以下三种方式分享你 App 的预览版。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Internal distribution&lt;/li&gt;
&lt;li&gt;TestFlight 内部测试&lt;/li&gt;
&lt;li&gt;TestFlight 外部测试&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;内部分发&quot;&gt;内部分发&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;通过内部分发的方式，每台测试设备需要使用临时的配置文件，并且每年只能使用此方法分发至最多 100 部 iPhone。&lt;/li&gt;
&lt;li&gt;临时配置文件的需要需要获取设备的 UDID。
要么你需要让用户自己通过 Mac Xcode 连接来获取，要么需要通过安装配置文件来获取（你需要建立和测试者之间的信任）。&lt;/li&gt;
&lt;li&gt;每次注册测试设备到 Apple，你都需要等待 Apple 来处理，这可能会花上一天的时间。&lt;/li&gt;
&lt;li&gt;每次注册完新的设备，你都需要重新进行 build。&lt;/li&gt;
&lt;li&gt;这种方式分发的应用需要用户在手机上开启开发者模式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;综上，这种方式只适用于很小范围内的内部测试。&lt;/p&gt;
&lt;h3 id=&quot;testflight-内部测试&quot;&gt;TestFlight 内部测试&lt;/h3&gt;
&lt;p&gt;TestFlight 内部测试需要你为测试者分配你的 Apple Developer 账号权限，它不需要将你的 App 提交审核。
所以它同样只适用于小范围的内部测试。&lt;/p&gt;
&lt;h3 id=&quot;testflight-外部测试&quot;&gt;TestFlight 外部测试&lt;/h3&gt;
&lt;p&gt;TestFlight 外部测试可以以多种方式来分发你的 App 到用户，比如通过邮箱添加或是链接添加，这也是最常见的外部测试方式。&lt;/p&gt;
&lt;p&gt;它的要求是你需要提交 App 到审核，提交时还显示需要你提供用于测试人员测试的账号，但实际上你可以忽略提交这个信息。
据我提交的体验来说，首次提交会需要一天的时间，但也不会不让通过。
后面的审核都是即时通过的机审，很方便。&lt;/p&gt;
&lt;p&gt;顺便一提，填联系信息时，手机号的报错并不正确，你只是需要添加上 +86。&lt;/p&gt;
&lt;h3 id=&quot;总结&quot;&gt;总结&lt;/h3&gt;
&lt;p&gt;在你想要分享你写的 App 给别人使用时，我推荐你首先尝试 TestFlight 外部测试来分发，即时你还没准备好审核。
如果首次审核直接过的话，那就皆大欢喜了。&lt;/p&gt;
&lt;p&gt;使用 expo 和 eas 来构建并提交 App 十分方便，你只需要：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; eas&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; build&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --profile&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; production&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --local&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; eas&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; submit&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; ios&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，别忘了更新你的 eas 配置：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;cli&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;appVersionSource&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;remote&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;development&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;developmentClient&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;distribution&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;internal&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;preview&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;distribution&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;internal&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;production&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;autoIncrement&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;submit&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;production&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;ios&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;ascAppId&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;123456&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>为什么是 ESLint</title><link>https://hyoban.cc/why-eslint</link><guid>why-eslint</guid><pubDate>Fri, 17 May 2024 16:14:11 GMT</pubDate><content:encoded>&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;
&lt;p&gt;这不是一篇比较 ESLint 和其他相关工具优劣进行拉踩的文章，而是介绍我选择使用 ESLint 来进行代码检查和格式化的原因。
虽然不可避免地会提到其他工具，但我觉得每个流行工具的存在都是因为它们有自己的特点和优势。
作为用户，我们只需要根据自己的需求和喜好来选择适合自己的工具。
如果工具存在问题，我们可以通过反馈和贡献来帮助改进。&lt;/p&gt;
&lt;h2 id=&quot;我看中的-eslint-的优势&quot;&gt;我看中的 ESLint 的优势&lt;/h2&gt;
&lt;h3 id=&quot;更不固执己见的格式化&quot;&gt;更不固执己见的格式化&lt;/h3&gt;
&lt;p&gt;关于为什么不使用如 &lt;a href=&quot;https://prettier.io&quot;&gt;Prettier&lt;/a&gt; 或者 &lt;a href=&quot;https://dprint.dev&quot;&gt;dprint&lt;/a&gt; 之类的格式化工具，我觉得 Anthony Fu 的 &lt;a href=&quot;https://antfu.me/posts/why-not-prettier&quot;&gt;Why I don’t use Prettier&lt;/a&gt; 一文已经说的足够清楚。
在此，我补充一点自己的想法。&lt;/p&gt;
&lt;p&gt;配置 &lt;code&gt;printWidth&lt;/code&gt; 并不能告诉 Prettier 我们希望在何时希望换行。
你通过增加 &lt;code&gt;printWidth&lt;/code&gt; 的值来回避一个不该换行的长字符串变量，就可能会导致一个你期望会换行的多参数函数的参数没有被正确地换行。&lt;code&gt;// prettier-ignore&lt;/code&gt; 的问题和 &lt;code&gt;// @ts-ignore&lt;/code&gt; 一样，我们虽然成功告诉工具这里不要换行，但同时也失去了这里本可以应用的其它格式化规则。&lt;/p&gt;
&lt;p&gt;尽管 Prettier 的哲学是用户不需要考虑格式化的配置选项，让你把代码放心的交给它来变得漂亮。
但实际上现有的可配置选项可能是 Prettier 能成为现在 js 社区最流行的格式化工具的原因之一。
想象一下，你会使用一个不能控制是用 &lt;code&gt;tab&lt;/code&gt; 还是 &lt;code&gt;space&lt;/code&gt; 来缩进的格式化工具吗？
考虑到 &lt;a href=&quot;https://github.com/prettier/prettier/issues/7475&quot;&gt;&lt;code&gt;tab&lt;/code&gt; 和 &lt;code&gt;space&lt;/code&gt; 之争&lt;/a&gt; 基本上是五五开的情况，如果没有这个选项的话，Prettier 至少会少一半的用户？&lt;/p&gt;
&lt;p&gt;此外，对于相对没那么多争论的选项，Prettier 的固执己见可能会给用户带来一些困扰。
例如 Prettier 会强制在文件末尾保留一行空行，这是 &lt;a href=&quot;https://github.com/prettier/prettier/issues/6360&quot;&gt;不可配置的&lt;/a&gt;。
尽管这个行为是大多数人所支持且有利于 git 等工具的，但对于无法忍受的人来说，他们不得不寻找一些 hack 的手段或者寻找别的工具来替代 Prettier。&lt;/p&gt;
&lt;p&gt;是的，我们有其它格式化工具可以选择，如 dprint，它的性能表现更好且提供更多的配置选项。
但同样的，它仍然没有解决何时代码应该换行的问题。
这不是个 bug，而是它们本身的工作模式所决定的。
此外，即便 dprint 配置选项更多，但你永远不可能满足所有人的需求，在 ESlint 的世界里，你可以轻松地通过配置现有规则的选项或是写插件来实现你自己的需求。&lt;/p&gt;
&lt;p&gt;当我注意到 ESLint Stylistic 现有的规则貌似没有处理 jsx 中一行多个参数间的空格时，我只需要把当前的代码放到 typescript-eslint 的 &lt;a href=&quot;https://typescript-eslint.io/play&quot;&gt;playground&lt;/a&gt; 中，然后根据右边的的 ESTree 展示的数据结构来写插件，就可以 &lt;a href=&quot;https://github.com/hyoban/eslint-plugin-hyoban/blob/main/src/jsx-attribute-spacing.ts&quot;&gt;实现自己的需求&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;可拓展性&quot;&gt;可拓展性&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/antfu7/status/1788023588244574262&quot;&gt;https://x.com/antfu7/status/1788023588244574262&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;通过使用性能更好的语言来重写原本用 js 写的工具，可以获得更好的性能。
但是，这通常是有代价的。
使用 Rust 来编写的 lint 工具一般无法轻松的自定义规则，所以只有在 ESLint 社区中极其流行的规则能够得到移植。
这意味着我们无法在第一时间拥有官方推出的 ESLint 插件，如随着 React Compiler 发布的 &lt;a href=&quot;https://github.com/facebook/react/tree/main/compiler/packages/eslint-plugin-react-compiler&quot;&gt;eslint-plugin-react-compiler&lt;/a&gt;；也无法拥有 ESLint 社区中小众但十分有用的插件，如 &lt;a href=&quot;https://eslint-plugin-command.antfu.me&quot;&gt;ESLint Plugin
Command&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;除了扩展规则之外，ESLint 还支持扩展 lint 的语言，如今你可以轻松的在 Vue，JSON，YAML，Toml，Markdown，Astro，Svelte 等等语言中使用 ESLint 来进行代码检查。
但对于使用原生语言所编写的 lint 工具来说，他们通常只能优先支持最主流的语言。
比如如果你使用 Biome，那么在你写 Vue 项目时你就暂时无法使用它，还是需要回到 ESLint。
我不太喜欢不同项目中使用不同工具导致的不一致性。&lt;/p&gt;
&lt;h3 id=&quot;优秀的生态&quot;&gt;优秀的生态&lt;/h3&gt;
&lt;p&gt;这一节中，我并不想再提及 ESLint 的插件生态有多么丰富，我们来聊聊 &lt;a href=&quot;https://github.com/microsoft/vscode-eslint&quot;&gt;ESLint VSCode&lt;/a&gt; 插件。
除了我们每天使用的保存自动修复功能，它还提供了一些其它有用的功能。&lt;/p&gt;
&lt;p&gt;在使用 ESlint 时，有些规则我们希望能自动修复，但却不是在保存时马上修复。
比如移除未使用的 import，又或是将 let 马上换成 const（我们可能很快就会对变量重新赋值）。
此时，我们可以使用 &lt;code&gt;eslint.codeActionsOnSave.rules&lt;/code&gt; 设置。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsonc&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;eslint.codeActionsOnSave.rules&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;!prefer-const&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;!unused-imports/no-unused-imports&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配合 &lt;code&gt;lint-staged&lt;/code&gt; 与 &lt;code&gt;simple-git-hooks&lt;/code&gt;，我们可以实现在编辑器中忽略部分规则，然后在 commit 之前将其自动修复。&lt;/p&gt;
&lt;p&gt;另一个十分有用的设置是 &lt;code&gt;eslint.rules.customizations&lt;/code&gt;。
前面我们关闭了部分规则的自动修复，但是编辑器还是会将其显示为错误。
通过这个设置，我们可以将这些规则的严重程度降低或是彻底关闭。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsonc&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;eslint.rules.customizations&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;@stylistic/*&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;off&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;@stylistic/no-tabs&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;@stylistic/max-statements-per-line&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;antfu/consistent-list-newline&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;off&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;prefer-const&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;off&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;unused-imports/no-unused-imports&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;off&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;rule&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;simple-import-sort/*&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;severity&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;off&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个设置对于能将 ESLint 作为一个代码格式化工具十分有用，我们可以直接关闭 ESLint Stylistic 中的规则在编辑器中的错误显示，同时保留它们的自动修复功能。
在 &lt;a href=&quot;https://github.com/microsoft/vscode-eslint/pull/1841&quot;&gt;下一个版本&lt;/a&gt; 中，它还允许你调整全部可自动修复的规则的严重程度。&lt;/p&gt;
&lt;h3 id=&quot;类型感知的-lint-规则&quot;&gt;类型感知的 lint 规则&lt;/h3&gt;
&lt;p&gt;基于 Rust 的 lint 工具很快，但是却没有使用类型信息进行 lint 的能力，Josh Goldberg 在 &lt;a href=&quot;https://www.joshuakgoldberg.com/blog/rust-based-javascript-linters-fast-but-no-typed-linting-right-now/&quot;&gt;Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now&lt;/a&gt; 一文中进行了十分详细的介绍。&lt;/p&gt;
&lt;p&gt;Oxlint 最近进行了尝试，&lt;del&gt;但是这似乎也导致了它回到了 JavaScript 的速度&lt;/del&gt;。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/boshen_c/status/1783632651506823204&quot;&gt;https://x.com/boshen_c/status/1783632651506823204&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Biome 开始准备实现 Type-aware linter。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/biomejs/status/1800858872896487889&quot;&gt;https://x.com/biomejs/status/1800858872896487889&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;2026 年 2 月更新，Oxlint 和 Biome 都已经实现了 type-aware linting 的功能了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://oxc.rs/blog/2025-12-08-type-aware-alpha.html&quot;&gt;Oxlint Type-Aware Linting Alpha&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://biomejs.dev/blog/biome-v2-0-beta/#nofloatingpromises&quot;&gt;Biome v2.0 beta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;我不那么在乎的-eslint-的缺点&quot;&gt;我不那么在乎的 ESLint 的“缺点”&lt;/h2&gt;
&lt;h3 id=&quot;性能&quot;&gt;性能&lt;/h3&gt;
&lt;p&gt;你可以看到非常多的 benchmark 展现出 Oxlint，Biome 等工具的性能远远超过 ESLint。
但是，从我的使用场景来看，性能问题好像没有那么重要。&lt;/p&gt;
&lt;p&gt;在编辑器中的实时 lint 和 precommit 时的 lint 一般都只需要对少量文件进行检查，我们可以将完整的 lint 过程交给 CI。
CI 并不会阻塞我们本地的开发流程，我们只需要在 CI 报错时再在本地对特定文件进行 lint 即可。&lt;/p&gt;
&lt;p&gt;我遇到的在编辑器中依然会出现性能问题的情况是，当项目逐渐变大，我们开启基于类型检查的规则会导致编辑器的保存操作出现明显的延迟。
但这时我们也不用完全妥协，关闭基于类型检查的规则。
ESLint Flat Config 的灵活性允许我们在编辑器中关闭特定的规则。
在终端或是 CI 环境中，我们依然可以进行完整的 lint。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; antfu&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; isInEditorEnv&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;@antfu/eslint-config&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; antfu&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  typescript&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    tsconfigPath&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;isInEditorEnv&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;tsconfig.json&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; undefined&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你也可以尝试一下 &lt;a href=&quot;https://github.com/johnsoncodehk/tsslint&quot;&gt;tsslint&lt;/a&gt; 或者 &lt;a href=&quot;https://github.com/ArnaudBarre/tsl&quot;&gt;tsl&lt;/a&gt;，它们是与 tsc 集成的带类型信息的 lint 工具。&lt;/p&gt;
&lt;h3 id=&quot;非官方推荐&quot;&gt;非官方推荐&lt;/h3&gt;
&lt;p&gt;ESLint 和 typescript-eslint 官方都决定 &lt;a href=&quot;https://eslint.org/blog/2023/10/deprecating-formatting-rules/&quot;&gt;废弃格式化代码相关的规则&lt;/a&gt;，同时他们并 &lt;a href=&quot;https://typescript-eslint.io/troubleshooting/formatting&quot;&gt;不推荐使用 ESLint 来进行格式化&lt;/a&gt;，而是推荐配合 Prettier 等格式化工具来使用。
但实际上我并不觉得这是一个问题，废弃这些规则并将其转到社区来维护实际上是个好事，我们现在有 &lt;a href=&quot;https://eslint.style&quot;&gt;ESLint Stylistic&lt;/a&gt; 这样的开箱即用的工具，并且它表现的非常好。&lt;/p&gt;
&lt;h3 id=&quot;配置复杂升级麻烦&quot;&gt;配置复杂，升级麻烦&lt;/h3&gt;
&lt;p&gt;最近的 ESLint 9.0 让很多人觉得 ESLint 的大版本升级十分复杂，主要遇到的问题是新的的配置文件格式导致我们需要重写写配置，以及 API 的 breaking change 导致很多使用的插件在 9.0 中无法使用。&lt;/p&gt;
&lt;p&gt;但是，我觉得这是一个短暂的问题，新的配置文件带来了很多有用的新工具和用法，这是利大于弊的。
如 &lt;a href=&quot;https://github.com/eslint/config-inspector&quot;&gt;ESLint Config Inspector&lt;/a&gt; 可以帮助我们更好编写和测试配置文件；可以根据项目安装的依赖来动态的生成配置（只在安装了 react 的项目中开启 react hooks 相关的规则）。&lt;/p&gt;
&lt;p&gt;API 的 breaking change 带来的问题也可以通过多种方式来解决：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;给上游插件写 PR 来适配 ESLint v9，很多情况下我们只需要修改几行代码，在 ESLint v9 中使用新的 API，保留旧的 API 作为兼容&lt;/li&gt;
&lt;li&gt;暂时使用 ESLint v8，等待插件插件适配（我们依然可以使用 Flat Config）&lt;/li&gt;
&lt;li&gt;使用官方推出的 &lt;a href=&quot;https://eslint.org/blog/2024/05/eslint-compatibility-utilities&quot;&gt;ESLint Compatibility Utilities&lt;/a&gt; 来帮助我们升级&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;结语&quot;&gt;结语&lt;/h2&gt;
&lt;p&gt;需要再次强调的是，这只是我的个人感受和观点，它可能存在考虑得不对的地方，欢迎你和我交流，也欢迎你分享你的观点。&lt;/p&gt;
&lt;p&gt;如果你现在想要尝试 ESLint All In One 的话，我十分推荐你从 Anthony Fu 的 ESLint config 起手，它支持非常多的语言和框架，你也可以在其基础之上灵活的进行配置。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antfu/eslint-config&quot;&gt;https://github.com/antfu/eslint-config&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;为了更开箱即用，我也为自己维护了一套 ESLint config。
它基于 @antfu/eslint-config，增加了一些自己的偏好，和开箱即用的 react &amp;#x26; tailwind 预设。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/eslint-config-hyoban&quot;&gt;https://github.com/hyoban/eslint-config-hyoban&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>xLog SDK 的一些进展</title><link>https://hyoban.cc/xlog-sdk</link><guid>xlog-sdk</guid><pubDate>Tue, 19 Mar 2024 09:51:53 GMT</pubDate><content:encoded>&lt;h2 id=&quot;为什么我们需要-xlog-sdk&quot;&gt;为什么我们需要 xLog SDK？&lt;/h2&gt;
&lt;p&gt;尽管我对于 xLog 的整体体验十分满意，但不得不说，相比于 Hexo、VitePress 之类静态生成的站点，它的访问速度还是有些慢。
目前来说，我们没有简单的方式来自己部署 xLog 或是利用 xLog 的数据生成静态站点。&lt;/p&gt;
&lt;p&gt;xLog 允许我们方便地通过 CSS 自定义网站的外观，但是当涉及到如何渲染页面时，这样的自定义程度就显得有限了。
在 &lt;a href=&quot;https://diygod.cc/unified-markdown&quot;&gt;如何优雅编译一个 Markdown 文档&lt;/a&gt; 一文中 diygod 介绍了 xLog 如何渲染 Markdown。
但是你可能会不满意代码高亮的样式，或是有支持高亮特定行的需求。
有的时候，你可以通过给 xLog 提 PR 的方式来满足你的需求，但不是所有的需求都适合 xLog 的每个用户。
如果我们能自己控制如何渲染 markdown 的话，就有了更多的自由度。&lt;/p&gt;
&lt;p&gt;对于很多人来说，xLog 不是他们唯一的博客、笔记或发布日常的平台，可能有人习惯使用 VS Code 来编辑博客，有人喜欢 Obsidian。
拥有一个 xLog SDK 的话，集成其它平台时，社区的作者就不需要每个人都去实现一遍 xLog server 的逻辑了。
这些重复性的工作应当通过 SDK 来统一解决。&lt;/p&gt;
&lt;h2 id=&quot;现在-sdk-具备的功能&quot;&gt;现在 SDK 具备的功能&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/sakuin&quot;&gt;https://github.com/hyoban/sakuin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;我发布了 sakuin 0.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;0 版本，你可以通过 npm 安装：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;ni&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; sakuin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从 sakuin 导入 &lt;code&gt;Client&lt;/code&gt; 并使用，它基于 &lt;code&gt;crossbell.js&lt;/code&gt;。
一般来说，你需要传递 &lt;code&gt;handle&lt;/code&gt; 来获取特定站点的数据，它是你的 xLog 子域名。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Client&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;sakuin&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; client&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; Client&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; site&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; client&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;site&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;getInfo&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;HANDLE&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你可以进行如下几类操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;获取站点信息 &lt;code&gt;client.site.getInfo&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;获取站点统计信息 &lt;code&gt;client.site.getStat&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;获取博客信息
&lt;ol&gt;
&lt;li&gt;获取全部博客 &lt;code&gt;client.post.getAll&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;分页获取博客 &lt;code&gt;client.post.getMany&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;获取单个博客 &lt;code&gt;client.post.get&lt;/code&gt; 或是 &lt;code&gt;client.post.getBySlug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;创建博客 &lt;code&gt;client.post.put&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;更新博客 &lt;code&gt;client.post.update&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;获取评论信息、发送匿名评论&lt;/li&gt;
&lt;li&gt;获取 short，portfolio 信息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在这里我无法介绍全部你可用的 API 和参数，你可以通过 TS 的类型来了解如何使用，后面我也会生成文档。&lt;/p&gt;
&lt;h2 id=&quot;我们用-sdk-能做什么&quot;&gt;我们用 SDK 能做什么？&lt;/h2&gt;
&lt;p&gt;sakuin 是从我的博客项目衍生出来的，所以很自然的我使用它来构建&lt;a href=&quot;https://hyoban.cc&quot;&gt;自己的博客&lt;/a&gt;，你可以对比它和原版 xLog 的访问速度。
此外，我按照自己的喜好重新实现了布局和文章的渲染，我拥有了完全可控的自由度。
对接 xLog 提供的匿名评论接口，我可以轻松的实现评论模块。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/UFNHbaBGdvz5Zmj.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;评论界面截图&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/vscode-xlog&quot;&gt;https://github.com/hyoban/vscode-xlog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;VS Code xLog 插件，通过这个插件，你可以下载全部的博客文章到本地，在本地编辑或创建你的博客。
你同样也可以在 VS Code 中上传本地文件或是远端链接文件到 IPFS。&lt;/p&gt;
&lt;h2 id=&quot;一些展望&quot;&gt;一些展望&lt;/h2&gt;
&lt;p&gt;关于 xLog SDK 的后续，我大概能想到如下几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进一步完善第三方与 xLog 进行交互所需要的 API，与 xLog 社区作者联系，为社区作者提供统一方便的开发体验。&lt;/li&gt;
&lt;li&gt;完整实现 xLog server 的能力，将项目转到 Crossbell 组织下，甚至替换 xLog server 的目前实现。&lt;/li&gt;
&lt;li&gt;为 SDK 提供 server API，使得我们可以不用通过 SDK 来进行交互。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;关于利用 SDK 集成 xLog 的想法有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;集成 git commit hook，实现自动创建更新博客。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;相信 xLog SDK 的出现能够让 xLog 更符合去中心化这个设定，有越来越多的人能真正的利用他们自己的数据，而不是只通过平台访问。
BTW，这篇博客就是我在 VS Code 中进行编写，使用 xLog 插件发布的。&lt;/p&gt;</content:encoded></item><item><title>我如何开始写一个 TypeScript 库</title><link>https://hyoban.cc/starter-ts</link><guid>starter-ts</guid><pubDate>Thu, 15 Feb 2024 07:14:39 GMT</pubDate><content:encoded>&lt;p&gt;要全自己折腾的话，或许会陷入无尽的坑，所以我选择从 antfu 的 &lt;a href=&quot;https://github.com/antfu/starter-ts&quot;&gt;starter-ts&lt;/a&gt; 开始，按照自己的习惯进行一些改造。&lt;/p&gt;
&lt;h2 id=&quot;技术栈选择&quot;&gt;技术栈选择&lt;/h2&gt;
&lt;h3 id=&quot;typescript--eslint--prettier&quot;&gt;TypeScript + ESLint + Prettier&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/microsoft/TypeScript&quot;&gt;TypeScript&lt;/a&gt; 自不必多说，我使用 &lt;a href=&quot;https://github.com/eslint/eslint&quot;&gt;ESLint&lt;/a&gt; 来检查和格式化代码。&lt;/p&gt;
&lt;p&gt;如果你和 &lt;a href=&quot;https://github.com/prettier/prettier&quot;&gt;Prettier&lt;/a&gt; 的 &lt;code&gt;printWidth&lt;/code&gt; 也做过斗争并且不能忍受的话，请看 &lt;a href=&quot;https://antfu.me/posts/why-not-prettier&quot;&gt;Why I don’t use Prettier&lt;/a&gt;，可能会喜欢用 ESLint 来格式化代码，并且我们现在有了 &lt;a href=&quot;https://eslint.style/&quot;&gt;ESLint Stylistic&lt;/a&gt; 这种开箱即用的配置。&lt;/p&gt;
&lt;p&gt;要了解更多，请查看 &lt;a href=&quot;https://hyoban.cc/why-eslint&quot;&gt;为什么是 ESLint&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;pnpm--bunchee--tsx--vitest&quot;&gt;pnpm + bunchee + tsx + vitest&lt;/h3&gt;
&lt;p&gt;为了方便的测试我们写的库，&lt;a href=&quot;https://github.com/pnpm/pnpm&quot;&gt;pnpm&lt;/a&gt; 的 &lt;a href=&quot;https://pnpm.io/workspaces&quot;&gt;workspace&lt;/a&gt; 是必不可少的，可以很方便的开一个 playground。
此外，它默认不提升依赖的特性也能够防止我们不小心引用了没有定义的依赖。
我的 &lt;code&gt;.npmrc&lt;/code&gt; 为：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;ignore-workspace-root-check&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;public-hoist-pattern&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;[]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://github.com/huozhi/bunchee&quot;&gt;bunchee&lt;/a&gt; 来完成打包任务，它读取 package.
json 中的 &lt;code&gt;exports&lt;/code&gt; 字段作为打包的输入输出，无需手动指定配置。
此外它的 esm 打包结果看起来也更好，tsup 存在 &lt;a href=&quot;https://github.com/egoist/tsup/issues/701&quot;&gt;ESM output with CJS content&lt;/a&gt; 的问题。&lt;/p&gt;
&lt;p&gt;如果你希望更清楚精细的控制打包流程，可以使用 &lt;a href=&quot;https://github.com/rollup/rollup&quot;&gt;rollup&lt;/a&gt; 配合一些插件来自己写配置。
这里有一些常用的插件推荐。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Swatinem/rollup-plugin-dts&quot;&gt;rollup-plugin-dts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/SukkaW/rollup-plugin-swc&quot;&gt;rollup-plugin-swc&lt;/a&gt; 或者 &lt;a href=&quot;https://github.com/egoist/rollup-plugin-esbuild&quot;&gt;rollup-plugin-esbuild&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rollup/plugins/tree/master/packages/node-resolve&quot;&gt;@rollup/plugin-node-resolve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rollup/plugins/tree/master/packages/commonjs&quot;&gt;@rollup/plugin-commonjs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你想看看类似 bunchee 的其它选择，可以看看 &lt;a href=&quot;https://github.com/unjs/unbuild&quot;&gt;unbuild&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/egoist/tsup&quot;&gt;tsup&lt;/a&gt;。
tsup 的 &lt;code&gt;--dts-resolve&lt;/code&gt; 选项在你想要打包一些依赖的时候很有用。&lt;/p&gt;
&lt;p&gt;开发过程中，非必要的情况下，基本上没人想先打包再跑代码。
因此，我使用 &lt;a href=&quot;https://github.com/privatenumber/tsx&quot;&gt;tsx&lt;/a&gt; 来直接执行 ts 文件，用 &lt;a href=&quot;https://github.com/vitest-dev/vitest&quot;&gt;vitest&lt;/a&gt; 来测试代码。&lt;/p&gt;
&lt;h2 id=&quot;正确设置-packagejson&quot;&gt;正确设置 &lt;code&gt;package.json&lt;/code&gt;&lt;/h2&gt;
&lt;h3 id=&quot;便捷地维护包的基本信息&quot;&gt;便捷地维护包的基本信息&lt;/h3&gt;
&lt;p&gt;作为一个起手模板，它需要提前写好包的基本信息，并可以在开一个新坑的时候快速的查找替换。&lt;/p&gt;
&lt;p&gt;基本信息处于两个位置，一个是 &lt;code&gt;package.json&lt;/code&gt;，一个是 &lt;code&gt;README.md&lt;/code&gt;，通过全局替换 &lt;code&gt;pkg-placeholder&lt;/code&gt; 和 &lt;code&gt;$description$&lt;/code&gt; 可以让你的包快速就位并发布。&lt;/p&gt;
&lt;h3 id=&quot;设置包导出的内容&quot;&gt;设置包导出的内容&lt;/h3&gt;
&lt;p&gt;首先你可以阅读 &lt;a href=&quot;https://antfu.me/posts/publish-esm-and-cjs&quot;&gt;Ship ESM &amp;#x26; CJS in one Package&lt;/a&gt;，&lt;a href=&quot;https://antfu.me/posts/types-for-sub-modules&quot;&gt;Types for Submodules&lt;/a&gt; 和 &lt;a href=&quot;https://juejin.cn/post/7221551421833314360&quot;&gt;moduleResolution 总结&lt;/a&gt; 这几篇文章来了解同时发布 esm 和 cjs 两种格式包相关的基础信息。
然后可以使用 &lt;a href=&quot;https://github.com/bluwy/publint&quot;&gt;publint&lt;/a&gt;，&lt;a href=&quot;https://github.com/arethetypeswrong/arethetypeswrong.github.io&quot;&gt;arethetypeswrong&lt;/a&gt;，&lt;a href=&quot;https://github.com/frehner/modern-guide-to-packaging-js-library&quot;&gt;modern-guide-to-packaging-js-library&lt;/a&gt; 这些工具来检查你的包是否符合规范。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; publint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; @arethetypeswrong/cli&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; attw&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt; --pack&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我最终得出的配置如下：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;sideEffects&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;types&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.d.ts&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.js&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;types&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.d.cts&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.cjs&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.cjs&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.js&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;types&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.d.ts&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;typesVersions&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;      &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/*&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./dist/index.d.ts&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;files&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dist&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;保持依赖定义正确&quot;&gt;保持依赖定义正确&lt;/h3&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://github.com/webpro/knip&quot;&gt;knip&lt;/a&gt; 查找项目中未使用的文件、依赖项和导出。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; knip&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://github.com/antfu/taze&quot;&gt;taze&lt;/a&gt; 来手动更新依赖或者使用 &lt;a href=&quot;https://docs.renovatebot.com&quot;&gt;renovatebot&lt;/a&gt; 来定时自动更新。&lt;/p&gt;
&lt;h2 id=&quot;自动修复-lint&quot;&gt;自动修复 lint&lt;/h2&gt;
&lt;p&gt;为了保证 commit 的代码符合代码规范，可以使用 &lt;a href=&quot;https://github.com/toplenboren/simple-git-hooks&quot;&gt;simple-git-hooks&lt;/a&gt; 来在 commit 之前进行检查。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;simple-git-hooks&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;pre-commit&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;pnpm lint-staged&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;lint-staged&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;eslint --fix&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;scripts&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;prepare&lt;/span&gt;&lt;span style=&quot;color:#99841877;--shiki-dark:#B8A96577&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;simple-git-hooks&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过这种方案存在的问题是，如果你的 ESLint 配置发生了变化，那 commit 的代码依然可能存在需要修复的问题。
所以我更倾向于在 CI 中进行检查。
使用 &lt;a href=&quot;https://github.com/stefanzweifel/git-auto-commit-action&quot;&gt;git-auto-commit-action&lt;/a&gt; 来自动修复并 apply 到当前分支。
这样还有一个好处是你不需要 require 其它贡献者完全设置好正确的环境。&lt;/p&gt;
&lt;h2 id=&quot;发版流程&quot;&gt;发版流程&lt;/h2&gt;
&lt;p&gt;要使我们的包版本号 +1，大抵有以下几个步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进行发版前的检查&lt;/li&gt;
&lt;li&gt;更新版本号&lt;/li&gt;
&lt;li&gt;发布到 npm&lt;/li&gt;
&lt;li&gt;commit &amp;#x26;&amp;#x26; tag&lt;/li&gt;
&lt;li&gt;写这次更新的日志，在 GitHub 上发布 release&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果每次都要手动做这些事情，那就太麻烦了，所以我们可以使用 &lt;a href=&quot;https://github.com/release-it/release-it&quot;&gt;release-it&lt;/a&gt; 来帮助我们自动化这些步骤。
借助 release-it 的 hooks 功能和自定义插件，我们能够方便的定义需要的发版流程。&lt;/p&gt;
&lt;p&gt;release-it 对于 pnpm workspace 的支持几乎为零，在 monorepo 发布多个包的情况下需要切换到其它方案，我写了个插件提供简单的支持。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/release-it-pnpm&quot;&gt;https://github.com/hyoban/release-it-pnpm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在这个插件中我还按照自己的喜好自动决定下一个版本号，以及整合 &lt;a href=&quot;https://github.com/antfu/bumpp&quot;&gt;bumpp&lt;/a&gt; 与 &lt;a href=&quot;https://github.com/antfu/changelogithub&quot;&gt;changelogithub&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;我的-starter&quot;&gt;我的 starter&lt;/h2&gt;
&lt;p&gt;我将我收拾完的 starter 开源了，如果你和我的喜好一样的话，可以基于它来改造自己的 starter。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/starter-ts&quot;&gt;https://github.com/hyoban/starter-ts&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>实现一个满意的深色模式切换按钮</title><link>https://hyoban.cc/dark-mode</link><guid>dark-mode</guid><pubDate>Thu, 04 Jan 2024 13:15:06 GMT</pubDate><content:encoded>&lt;h2 id=&quot;它会是什么样子&quot;&gt;它会是什么样子&lt;/h2&gt;
&lt;p&gt;一：从外观和交互上来说：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只有一个按钮，通过单击的方式来切换，而不是一个三选的 Dropdown Menu&lt;/li&gt;
&lt;li&gt;服务端渲染友好，按钮能直接反映当前主题是否为深色&lt;/li&gt;
&lt;li&gt;页面刷新时不会出现闪烁&lt;/li&gt;
&lt;li&gt;切换时页面颜色整体过渡，不会出现不一致&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;二：从处理逻辑上来说：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户偏好可以持久化到浏览器存储&lt;/li&gt;
&lt;li&gt;用户偏好可以无感的恢复到系统偏好&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/ZyQXY2EUq5aphm3.gif&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;ScreenShot 2024-01-04 19.10.00&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;我会使用 &lt;a href=&quot;https://jotai.org&quot;&gt;Jotai&lt;/a&gt; 来实现，我喜欢 Jotai。&lt;/p&gt;
&lt;h2 id=&quot;获取系统偏好状态&quot;&gt;获取系统偏好状态&lt;/h2&gt;
&lt;p&gt;通过 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries#targeting_media_features&quot;&gt;Media queries&lt;/a&gt; 的 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&quot;&gt;prefers-color-scheme&lt;/a&gt; 和 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia&quot;&gt;matchMedia&lt;/a&gt; 来获取系统偏好是否为深色。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jotai.org/docs/core/atom&quot;&gt;onMount&lt;/a&gt; 函数会在 atom 被订阅时执行，在取消订阅时执行其返回的函数。
加上判断浏览器环境的逻辑来兼容服务端渲染。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atomSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  isSystemDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;onMount&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;undefined&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;      return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;matcher&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;matchMedia&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;update&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;      set&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;matcher&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;matches&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;    update&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    matcher&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;change&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; update&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      matcher&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;removeEventListener&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;change&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; update&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; isSystemDarkAtom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;什么时候应该是深色&quot;&gt;什么时候应该是深色&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;用户的偏好为深色&lt;/li&gt;
&lt;li&gt;系统偏好为深色，且用户偏好不为浅色&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;light&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; isDarkMode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;setting&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; setting&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;!!&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; setting&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;light&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;深色状态的读取和切换&quot;&gt;深色状态的读取和切换&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;读取用户的主题偏好和系统偏好来判断当前是否为深色模式，用户主题偏好使用 &lt;a href=&quot;https://jotai.org/docs/utilities/storage&quot;&gt;atomWithStorage&lt;/a&gt; 存储到浏览器（Fixes 2-1）。&lt;/li&gt;
&lt;li&gt;借助 &lt;a href=&quot;https://github.com/jotaijs/jotai-effect&quot;&gt;jotai-effect&lt;/a&gt; 来处理副作用，同步状态到 html 页面，并在用户偏好和当前系统偏好一致时，将用户偏好恢复到系统偏好（Fixes 2-2）。&lt;/li&gt;
&lt;li&gt;切换按钮的点击回调不接收参数，根据当前用户偏好和系统偏好来更新用户偏好。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atomDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atomSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;themeAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atomWithStorage&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;storageKey&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;toggleDarkEffect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atomEffect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;themeAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; isDarkMode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    document&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;documentElement&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;classList&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;toggle&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; isDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; === &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      || &lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; === &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;light&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26; !&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;      set&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;themeAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;      get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;toggleDarkEffect&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;themeAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; isDarkMode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;themeAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;      set&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        themeAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        theme&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isSystemDark&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;light&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;system&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;我们有能用的-hook-了&quot;&gt;我们有能用的 hook 了&lt;/h2&gt;
&lt;p&gt;相比于直接使用 atom，为 &lt;code&gt;atomDark&lt;/code&gt; 创建一个自定义 hook 会是一个更好的选择。
因为 Jotai 的 write 函数是没有响应式的，直接使用 atom 可能会只使用到 &lt;code&gt;toggleDark&lt;/code&gt; 函数，此时读取到的状态是不正确的。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; atomDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useAtomValue&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;toggleDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useSetAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isDarkAtom&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;isDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;toggleDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;来一个按钮&quot;&gt;来一个按钮&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;使用 &lt;a href=&quot;https://github.com/egoist/tailwindcss-icons&quot;&gt;tailwindcss-icons&lt;/a&gt; 和 &lt;a href=&quot;https://lucide.dev&quot;&gt;Lucide&lt;/a&gt; 来引入图标表示当前主题状态（Fixes 1-1）。&lt;/li&gt;
&lt;li&gt;通过使用 tailwind 的 &lt;a href=&quot;https://tailwindcss.com/docs/dark-mode&quot;&gt;Dark Mode&lt;/a&gt; 支持来正确显示状态图标而不读取 &lt;code&gt;isDark&lt;/code&gt; 状态使得服务端渲染友好（Fixes 1-2）。&lt;/li&gt;
&lt;li&gt;加一点 Transition 动画效果。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; AppearanceSwitch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; toggleDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; useDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;toggleDark&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;i-lucide-sun scale-100 dark:scale-0 transition-transform duration-500 rotate-0 dark:-rotate-90&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;i-lucide-moon absolute scale-0 dark:scale-100 transition-transform duration-500 rotate-90 dark:rotate-0&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;页面闪烁怎么解决&quot;&gt;页面闪烁怎么解决？&lt;/h2&gt;
&lt;p&gt;当浏览器加载的页面样式和用户偏好不一致时，就会出现页面闪烁，更新样式的情况。
我们需要在页面加载前注入脚本来确保主题正确。
（Fixes 1-3）&lt;/p&gt;
&lt;p&gt;如果你使用 Vite，可以在 &lt;code&gt;index.html&lt;/code&gt; 中注入脚本：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  !&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; e&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;matchMedia&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;matchMedia&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;matches&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      t&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; localStorage&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;getItem&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;use-dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;&quot;system&quot;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; document&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;documentElement&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;classList&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;toggle&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  })()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你使用 Next.js，可以使用 &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; 来注入脚本。
值得一提的是，我们需要使用 &lt;code&gt;suppressHydrationWarning&lt;/code&gt; 来忽略 React 在客户端水合时的警告。
因为我们在客户端切换了 &lt;code&gt;html&lt;/code&gt; 节点的 &lt;code&gt;className&lt;/code&gt;，这可能会和服务端渲染的结果不一致。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; ThemeProvider&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; children&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }: { &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;ReactNode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;script&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        dangerouslySetInnerHTML&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;={{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;          __html&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;here&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;        }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; RootLayout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; children&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }: { &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;React&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;ReactNode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; })&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; suppressHydrationWarning&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;ThemeProvider&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;ThemeProvider&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;在切换时禁用-transition&quot;&gt;在切换时禁用 transition&lt;/h2&gt;
&lt;p&gt;在 &lt;a href=&quot;https://paco.me/writing/disable-theme-transitions&quot;&gt;Disable transitions on theme toggle&lt;/a&gt; 一文中，已经十分详细的解释了这样做的原因。
因为我们不希望在切换时部分组件的颜色过渡和页面主题的过渡节奏不一致。
（Fixes 1-4）&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/before.gif&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;transition demo&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;这很好，但是我们的主题切换按钮有用到 &lt;code&gt;transition&lt;/code&gt;，我们需要能给部分组件开白名单，可以使用 css 的 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:not&quot;&gt;:not&lt;/a&gt; 伪类来实现。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt; * credit: https://github.com/pacocoursey/next-themes/blob/cd67bfa20ef6ea78a814d65625c530baae4075ef/packages/next-themes/src/index.tsx#L285&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; disableAnimation&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;disableTransitionExclude&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[] =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [])&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;css&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; document&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;createElement&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  css&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;append&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    document&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;createTextNode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;      `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;disableTransitionExclude&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;s &lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; `&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;:not(&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;  -webkit-transition: none !important;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;  -moz-transition: none !important;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;  -o-transition: none !important;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;  -ms-transition: none !important;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;  transition: none !important;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;      `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  document&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;append&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;css&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;    // Force restyle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    (()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;document&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;))()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;    // Wait for next tick before removing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;    setTimeout&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(()&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      css&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    },&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;最后&quot;&gt;最后&lt;/h2&gt;
&lt;p&gt;处理完以上问题，我就有了一个满意的深色模式切换按钮了。
我将其发布到了 &lt;a href=&quot;https://www.npmjs.com/package/jotai-dark&quot;&gt;npm&lt;/a&gt; 上，你可以直接使用。
你可以在 GitHub 上查看完整的代码和示例。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/jotai-dark&quot;&gt;https://github.com/hyoban/jotai-dark&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>写在 24 岁之前</title><link>https://hyoban.cc/24-years</link><guid>24-years</guid><description>我快 24 岁了，想着要回顾一下自己的过去，再思考一下自己的未来要走向哪里，就写下这篇文章。</description><pubDate>Fri, 29 Sep 2023 16:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
虽然大家看到的时候，我已经 24 了，但我真是在 24 之前写的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;del&gt;我快 24 岁了&lt;/del&gt;，想着要回顾一下自己的过去，再思考一下自己的未来要走向哪里，就写下这篇文章。&lt;/p&gt;
&lt;h2 id=&quot;开源&quot;&gt;开源&lt;/h2&gt;
&lt;p&gt;参与自己力所能及的开源项目已经变成我生活的一部分，我主要的切入点是自己使用开源项目时遇到的问题和冒出的想法。
下面我简单举几个例子。&lt;/p&gt;
&lt;p&gt;我十分喜欢 xLog 这个平台，并将其作为我的主要博客站点。
在使用过程中我偶尔会遇到一些小问题，或是自己想要一些小功能，就把代码拿过来改一改提 PR。
比如在通过浏览器使用在线编辑器来写博客时，我发现编辑和预览区域所占的宽度太小，编辑体验不佳，就增加了专注模式。
现在开启专注模式，就可以使得页面全屏，同时隐藏与编辑区无关的元素。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/L9YEJSs1jwClNR3.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Focus Mode demo&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;又比如 &lt;code&gt;Callout&lt;/code&gt; 在很多站点生成器（如 VitePress）都是内置的语法，因此我就找了个 remark 插件支持了一下，现在你可以使用如下的写法来渲染一个 &lt;code&gt;Callout&lt;/code&gt;。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:::note&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;同样可以写 **markdown**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:::&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
现在还支持 &lt;a href=&quot;https://github.com/Crossbell-Box/xLog/pull/1095&quot;&gt;GitHub Alert&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&gt; [!NOTE]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&gt; Something&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/egoist/tailwindcss-icons&quot;&gt;https://github.com/egoist/tailwindcss-icons&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tailwindcss-icons&lt;/code&gt; 是一个 &lt;a href=&quot;https://unocss.dev/presets/icons&quot;&gt;UnoCSS Icons preset&lt;/a&gt; 在 Tailwind 这边类似的工具，我几乎每个项目都会使用它。
使用的过程中遇到 TS 类型没有被正确找到的问题，但是提了 PR 后发现 issue 和 PR 好像有几个月没有被作者看了。&lt;/p&gt;
&lt;p&gt;起初，我选择 fork 一份自己发个包。
但是我马上觉得，这个小修复应当是会有益于其他使用这个项目的人的。
所以，我选择在 Twitter 上联系作者，询问能不能拉我进仓库，使我能够帮忙处理一些小问题。
很幸运，作者马上同意了。
于是我开始给它增加一些自定义选项，修复一些 bug，觉得成就感满满。&lt;/p&gt;
&lt;p&gt;类似的例子还有不少，比如做一个 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3336&quot;&gt;rsuite 的 good first issue&lt;/a&gt;，给 star-history 增加 &lt;a href=&quot;https://github.com/star-history/star-history/pull/316&quot;&gt;深色模式&lt;/a&gt;，使得 sonner 能够 &lt;a href=&quot;https://github.com/emilkowalski/sonner/pull/134&quot;&gt;同时在多个位置弹出 Toast&lt;/a&gt;。
我觉得参与开源有一万个好处，不信我数给你听：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;因为都是在自己喜欢的领域来解决自己的问题或是帮助他人，和完成工作上的需求完全不同，所以写起来动力满满。&lt;/li&gt;
&lt;li&gt;以一个小的需求或者 bug 入手，有助于你快速切入一个相对较大的项目，为以后做出更重要的贡献打下了基础。&lt;/li&gt;
&lt;li&gt;因为你开始尝试解决此前没处理过的问题，你能得到很快速的成长，作者的 review 也能让你学习到更多平时不熟悉的知识。&lt;/li&gt;
&lt;li&gt;你会和更多志趣相投的小伙伴链接起来，一同交流成长。&lt;/li&gt;
&lt;li&gt;开源经历作为你解决问题的能力的证明，也有助于你面试找工作，或是得到别人的认可。&lt;/li&gt;
&lt;li&gt;等等等等…&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;秋招&quot;&gt;秋招&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://hyoban.cc/work-one-year&quot;&gt;https://hyoban.cc/work-one-year&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在远程兼职一年后一文中，我介绍了我远程兼职一年来的体验，这里就不再提了。
写完后的九月份也就刚好到了秋招的时候，本着「不面白不面，面了还能看看自己水平」的想法，我就开始投简历了。
大概投了十几个互联网公司后，逐渐能收到笔试面试了。
我最终面了快手，百度，美团三个，时间线大概是这样的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;美团笔试，前端考的算法题少且简单，两个算法题都做出来了。&lt;/li&gt;
&lt;li&gt;快手一面（没有笔试）下午面完，晚上网页就上就挂了。&lt;/li&gt;
&lt;li&gt;百度一面，和面试官聊的特别好，我觉得没有理由不让我过（也确实进二面了）。&lt;/li&gt;
&lt;li&gt;美团一面，美团的面试氛围很好，内容包含基础题和项目经验，给面试结果的响应也很快。&lt;/li&gt;
&lt;li&gt;百度二面，二面问了些我不擅长的地方，答得也不好，最后挂了。&lt;/li&gt;
&lt;li&gt;然后就只有美团的面试了，一面到三面，然后 hr 面，一直到收到意向邮件。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;收到美团的意向之后，我就没有再面别的公司了，收割多个 offer 不是我的目标，并且看面经、准备一轮轮面试也很消耗精力。
至于美团的整个面试流程，最重要的是考察对于基础知识的掌握程度和实习项目经验，倒没有手撕算法的环节也算是幸运了。
在我的 &lt;a href=&quot;https://twitter.com/0xhyoban/status/1708985656687600111&quot;&gt;这篇推&lt;/a&gt; 里，我也表示，如果不断拷打我算法和八股的话，我估计什么 offer 也拿不到。&lt;/p&gt;
&lt;p&gt;回过头一看，秋招的经历和我参与工作的经历也很类似。
我没有刻意准备秋招（刷力扣、背八股），只是做些我喜欢做的事情（工作、参与开源）。
面试过程中，关于我远程工作和开源项目的部分被询问了很多。
我想，如果没有这些，我显然是过不了面试的，毕竟我的学历和校园经历很平庸。
这使得我更加坚信，我应该做更多我喜欢的事情，而不是变得世俗，变得功利。
过往的每一点经历在未来总会发挥它的作用的。&lt;/p&gt;
&lt;h2 id=&quot;经济&quot;&gt;经济&lt;/h2&gt;
&lt;p&gt;拥有一份待遇还不错的远程工作对于一个穷学生来说，真的是如雪中送碳一般。
我不仅终于经济独立，还能够给予家里一些帮助。
今年更新了我的主要数码设备（上半年换了 MacBook 14 寸 M1 Pro，下半年买了 iPhone 15 标准版），也给我妈换了小米 13 ultra，在我爸需要钱周转的时候马上打钱支持一下。
我不再需要为了节省生活费考虑每天吃饭的开销，也不需要考虑学校里麻烦且酬劳低的兼职，能够更专心的投入自己喜欢的事情。&lt;/p&gt;
&lt;p&gt;大学的时候，我特别喜欢折腾数码产品，经常上闲鱼上淘一些感兴趣的东西。
但是，因为家里提供的生活费就那么多，大多也就是折腾玩一下就又二手出掉了。
现在在经济上没什么压力，但是却不太能找到折腾的那股劲，甚至游戏也不玩了。&lt;/p&gt;
&lt;h2 id=&quot;学业&quot;&gt;学业&lt;/h2&gt;
&lt;p&gt;对我来说，我处理的最糟糕的部分就是我的学业了，我到现在还没有搞定自己的小论文。&lt;/p&gt;
&lt;p&gt;我清楚认识到自己没有科研的天分，也对做研究提不起兴趣，可是知道的有点晚。
大学期间，我还没有找到自己在编程领域最喜欢的部分，觉得自己还找不到一份不错的工作，也都还没认识现在的小伙伴们，所以我选择延长自己的学习生涯作为缓冲。
但是现在的我只想快一点结束我的学生生涯。&lt;/p&gt;
&lt;h2 id=&quot;感情&quot;&gt;感情&lt;/h2&gt;
&lt;p&gt;我曾经喜欢过几位女生，但是还没有喜欢我的女生出现。
我的家里都在催着我赶紧找对象，我的亲戚也在我我每次回家的时候给我介绍相亲对象。
我对此并不反感，把相亲看作结识新朋友的方式，与参加其它活动的形式并无区别。
但是我的相亲对象往往不这么想，因此几天一过一般都变成聊天列表里不会再打开的联系方式了。&lt;/p&gt;
&lt;p&gt;我认同&lt;a href=&quot;https://book.douban.com/subject/27039296/&quot;&gt;《幸福的勇气》&lt;/a&gt;一书中提到的「爱并非被动坠入」一言，无条件的信赖别人，并期待着「无条件信赖我，能一起建立不可分割的“我们的幸福”」的人出现。&lt;/p&gt;
&lt;h2 id=&quot;社交&quot;&gt;社交&lt;/h2&gt;
&lt;p&gt;最近，我尝试在 Twitter 上变得活跃一些，去与更多小伙伴建立链接。
通过工作的小伙伴，我在 &lt;a href=&quot;https://podcasts.apple.com/be/podcast/echo-js/id1516139979&quot;&gt;Echo.js&lt;/a&gt; 蹭上了嘉宾的位子，期待即将能在技术播客月参与一档播客节目的录制。
但我好像还是个不善于社交的人，常觉得对着电脑写代码更加自在。&lt;/p&gt;
&lt;h2 id=&quot;未来&quot;&gt;未来&lt;/h2&gt;
&lt;p&gt;我还可以，还应该做些什么样的事情呢？
我不喜欢孤独，希望自己不要那么宅，走出去不同的地方，见不同的人，做不同的事情。&lt;/p&gt;
&lt;p&gt;这应该就可以概括我未来的目标了，可能是培养起读书的习惯；可能是玩几款自己喜欢的游戏；可能是去锻炼身体，去运动；可能是去旅游；可能是去面基网上的小伙伴，一起吃个饭畅谈技术，畅谈人生；等等等等。&lt;/p&gt;
&lt;p&gt;:::assert
如果你有什么想和 24 岁的我交流的话，欢迎通过页脚的链接与我联系。
:::&lt;/p&gt;</content:encoded></item><item><title>你可以用 Tailwind 来学习 CSS</title><link>https://hyoban.cc/learn-css-with-tailwind</link><guid>learn-css-with-tailwind</guid><pubDate>Wed, 16 Aug 2023 13:06:42 GMT</pubDate><content:encoded>&lt;h2 id=&quot;如果你还不太懂-html-和-css&quot;&gt;如果你还不太懂 HTML 和 CSS&lt;/h2&gt;
&lt;p&gt;HTML 大概是长成下面这样子的。
每个 HTML 标签都有自己的含义（如表示图片或者链接）；或由一组标签包围，或者自闭合；可以层级嵌套。
N 多个 HTML 标签共同组合而成为你在浏览器中看到的网页。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; alt&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;A dog on an iPad&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/assets/images/dog-ipad.jpg&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;Here is &lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;https://example.com&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;a link&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你对有哪些常用的 HTML 标签感兴趣，可以看这个 Simple.css 的 &lt;a href=&quot;https://simplecss.org/demo&quot;&gt;Demo 网页&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;但是，如果只有 HTML，你的网页可能看起来会非常简陋，只有浏览器所提供的默认样式。
所以，我们需要 CSS 来为我们的网页添加样式。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  color&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;333&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  font-size&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 16&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  line-height&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.text&lt;/code&gt; 是一个 CSS 类选择器，它会匹配到 HTML 中 &lt;code&gt;class&lt;/code&gt; 中包含 &lt;code&gt;text&lt;/code&gt; 的标签，从而为其添加样式。
这里我们简单设置了一下文本的颜色，大小和行高。&lt;/p&gt;
&lt;p&gt;结合 HTML 和 CSS，你就已经可以做出好看的静态网页了。
如果你对还有哪些 CSS 的语法感兴趣，别急，看完这边文章你会得到一个极佳的学习文档。&lt;/p&gt;
&lt;h2 id=&quot;tailwind-是什么&quot;&gt;Tailwind 是什么&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以上是 Tailwind 的官方介绍。
简单来说，它就是一个原子化 CSS 框架，提供了很多工具类来让你可以直接在 HTML 为你的网页添加样式。&lt;/p&gt;
&lt;p&gt;在开始介绍如何通过 Tailwind 学习 CSS 之前，我们先来看看 Tailwind 帮我们做了什么：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;扫描你的 HTML 文件，找到所有的 &lt;code&gt;class&lt;/code&gt; 属性&lt;/li&gt;
&lt;li&gt;根据这些 &lt;code&gt;class&lt;/code&gt; 属性中匹配的工具类，如 &lt;code&gt;flex m-1&lt;/code&gt;，生成对应的 CSS 内容&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;是的，就这么简单。
你完全可以在不使用 Tailwind 的情况下，手动写出 CSS 的内容。
它不像 Vue.js 或者 React 那样，理解它们所做的事情需要付出很大的学习成本。&lt;/p&gt;
&lt;h2 id=&quot;为什么要用-tailwind-来学习-css&quot;&gt;为什么要用 Tailwind 来学习 CSS&lt;/h2&gt;
&lt;p&gt;与通过 MDN 的文档来学习相比：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tailwind 的文档更加简洁，更多图例；MDN 的文档更加详细，更多概念。
&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/NhYoerOTa6ZjFRB.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;justify-start doc&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/li&gt;
&lt;li&gt;Tailwind 分类更加清晰，更能突出重点；MDN 的文档更加全面，但是容易让人迷失。
&lt;ul&gt;
&lt;li&gt;CSS 的知识点很多，但是日常开发中我们只会用到其中的一部分。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tailwind 的文档有助于你更快了解一些关键概念，如 &lt;a href=&quot;https://tailwindcss.com/docs/dark-mode&quot;&gt;深色模式支持&lt;/a&gt; 和 &lt;a href=&quot;https://tailwindcss.com/docs/responsive-design&quot;&gt;响应式设计&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;Tailwind 的 &lt;a href=&quot;https://tailwindcss.com/docs/theme&quot;&gt;主题系统&lt;/a&gt; 有助于你写出更有设计感的页面。&lt;/li&gt;
&lt;li&gt;Tailwind 提供一个功能强大的 &lt;a href=&quot;https://play.tailwindcss.com&quot;&gt;Playground&lt;/a&gt;，让你可以直接在浏览器中进行学习。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;如果你是个设计师&quot;&gt;如果你是个设计师&lt;/h3&gt;
&lt;p&gt;作为一个设计师，如果你只会使用设计工具来做设计的话，那么可能会遇到的一个问题是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;开发：你这个设计我不好实现啊！！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是，如果你用代码做设计，他再敢这么说，你就直接把 Tailwind Playground 的链接丢给他，看他还敢多说话。&lt;/p&gt;
&lt;p&gt;同时，和开发用一套技术意味着你们能获得效率上的提升：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;开发不需要自己将整套设计一点点的对应到代码实现中&lt;/li&gt;
&lt;li&gt;设计能确保开发百分百还原自己的设计&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;最后要强调的&quot;&gt;最后要强调的&lt;/h2&gt;
&lt;p&gt;并非所有的 Tailwind 工具类都十分直观且简单，就好像 &lt;code&gt;flex-row&lt;/code&gt; 等于 &lt;code&gt;flex-direction: row;&lt;/code&gt;一样。&lt;code&gt;grid-cols-1&lt;/code&gt; 对应生成的 CSS 要稍复杂一些，它等于 &lt;code&gt;grid-template-columns: repeat(1, minmax(0, 1fr));&lt;/code&gt;。
在你没有理解它帮你生成的 CSS 的含义之前，请不要继续只看 Tailwind 的文档，你应该做的是去 MDN 的文档中学习这个属性的具体含义。&lt;/p&gt;
&lt;p&gt;在 Playground 中，你可以通过自动补全和将鼠标移上去的方式来看到某个工具类对应生成的 CSS。
所以，开始学习 Tailwind 吧，即使你不会 CSS。
它优秀的文档和 Playground 会成为你学习的好帮手。&lt;/p&gt;
&lt;h2 id=&quot;一个插件&quot;&gt;一个插件&lt;/h2&gt;
&lt;p&gt;在 vscode 中，你可以通过安装我写的一个插件来看到哪些文本会生成对应的 CSS。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hyoban/tailwindcss-classname-highlight&quot;&gt;https://github.com/hyoban/tailwindcss-classname-highlight&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>如何让 fetch 变得类型安全</title><link>https://hyoban.cc/type-safe-fetch</link><guid>type-safe-fetch</guid><description>总所周知，发网络请求传递参数和获取返回值时只能凭感觉，但是我们可以做一点类型体操来解决这个问题</description><pubDate>Wed, 09 Aug 2023 04:56:48 GMT</pubDate><content:encoded>&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;https://jsonplaceholder.typicode.com/todos/1&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; console&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当你直接使用如上的代码来发起网络请求时，你会发现你的 IDE 无法给你提供任何关于 &lt;code&gt;json&lt;/code&gt; 的类型提示。
这很合理，没人知道这个接口会返回什么格式的数据。
于是你需要进行如下几个步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;手动测试这个接口，获取返回值&lt;/li&gt;
&lt;li&gt;根据返回值的内容，利用如 &lt;a href=&quot;https://transform.tools/json-to-typescript&quot;&gt;transform.tools&lt;/a&gt; 之类的工具生成对应的类型定义&lt;/li&gt;
&lt;li&gt;将类型定义复制到你的项目中&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;as&lt;/code&gt; 来标识返回值的类型&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样下来你也只是能获得接口正常返回时 &lt;code&gt;json&lt;/code&gt; 的类型而已，还有很多问题需要被解决：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;手动请求得到返回值不够准确，比如调用接口出错时，返回值的类型可能会发生变化&lt;/li&gt;
&lt;li&gt;请求的 &lt;code&gt;url&lt;/code&gt; 和输入的参数没有任何类型提示&lt;/li&gt;
&lt;li&gt;当后端接口变动时，tsc 无法给出报错&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;解决方案的基础&quot;&gt;解决方案的基础&lt;/h2&gt;
&lt;p&gt;ts 中有个非常好用的功能是 &lt;code&gt;as const&lt;/code&gt; 断言，给变量添加这个断言后，变量的类型会被推断为更具体的字面量类型，同时会禁止对变量的修改。
下面是一个简单的例子，可以看到 &lt;code&gt;navigateTo&lt;/code&gt; 这个函数的参数变的更加安全了，我们不能传递一个未被定义的路由名称字符串。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routes&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  home&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  about&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/about&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;declare&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; navigateTo&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;keyof&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; routes&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;):&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;navigateTo&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;404&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;// Argument of type &apos;&quot;404&quot;&apos; is not assignable to parameter of type &apos;&quot;home&quot; | &quot;about&quot;&apos;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就是我们能实现类型安全的 fetch 的基础。&lt;/p&gt;
&lt;h2 id=&quot;定义路由结构&quot;&gt;定义路由结构&lt;/h2&gt;
&lt;p&gt;首先我们需要定义一个路由结构，这个结构包含了一个接口的全部必要信息（请求路径，输入，输入，以及可能出现的错误码）。
同时这里我们使用 &lt;code&gt;zod&lt;/code&gt; 来进行运行时的数据格式校验。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;// add.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;zod&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/add&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  a&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  b&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;  result&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;errCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;NO_LOGIN&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定义多个接口之后，在 &lt;code&gt;index.ts&lt;/code&gt; 中导出所有的接口。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;// index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Add&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./add&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们就能拿到全部的路由信息&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; routesWithoutPrefixObj&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;./interface/index&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routesWithoutPrefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Object&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;values&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  routesWithoutPrefixObj&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; ValueOfObjectArray&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routesWithoutPrefixObj&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;定义公共类型&quot;&gt;定义公共类型&lt;/h2&gt;
&lt;p&gt;定义通用返回结构，公共前缀和未知错误码。
我们并不是接口直接返回数据，而是返回一个包含了错误码和数据的对象。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routesWithoutPrefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; Object&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;values&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  routesWithoutPrefixObj&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; ValueOfObjectArray&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routesWithoutPrefixObj&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;prefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/api&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Prefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; prefix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;unknownError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;UNKNOWN_ERROR&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; OutputType&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Err&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[]&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;ArrayToUnion&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; unknownError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  |&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;计算出带前缀的实际路由&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routes&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; routesWithoutPrefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;    path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;prefix&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}${&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;})&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; AddPathPrefixForRoutes&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;routesWithoutPrefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;转换路由结构&quot;&gt;转换路由结构&lt;/h2&gt;
&lt;p&gt;到此为止，我们只是拿到了全部路由对象组成的数组，这对于我们实现类型安全的 fetch 来说并不方便。
我们需要将这个数组转换成一个对象，这个对象的 key 是路由的请求路径，value 是路由的其它信息。
如此，调用 fetch 时，我们就能获得更好的补全体验，在输入确定的路径之后，能得到正确的输入类型和输出类型。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RouteItem&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;AnyZodObject&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;AnyZodObject&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  errCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;readonly&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Helper&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RouteItem&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;    T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      errCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;errCode&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      output&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;OutputType&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;z&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&gt;, &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;errCode&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; DistributedHelper&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RouteItem&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Helper&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; UnionToIntersection&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  DistributedHelper&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;ArrayToUnion&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; routes&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如此我们就能得到最终包含全部路由信息的类型 &lt;code&gt;Route&lt;/code&gt;，这个类型的大致结构如下：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/api/add&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        a&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        b&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        result&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    errCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;readonly&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;NO_LOGIN&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    output&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;OutputType&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;        result&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }, &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;readonly&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;NO_LOGIN&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;/api/dataSet/delDataSet&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    ...;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt; ... &lt;/span&gt;&lt;span style=&quot;color:#2F798A;--shiki-dark:#4C9A91&quot;&gt;13&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; more&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;.. &lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#393A34;--shiki-dark:#DBD7CAEE&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;为-fetch-添加类型&quot;&gt;为 fetch 添加类型&lt;/h2&gt;
&lt;p&gt;可以看到，只需要约束 fetch 的第一个参数 path 为 &lt;code&gt;keyof Route&lt;/code&gt;，第二个参数为对应路由的输入类型，就能得到正确的输出类型。
为了实现上的简单，所有的 http 请求都使用 &lt;code&gt;POST&lt;/code&gt; 方法，所有的输入参数都从 body 中传递。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; myFetch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Path&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; keyof&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  try&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      method&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;POST&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      headers&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;      body&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; instanceof&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; FormData&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; input&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    const &lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;output&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;      throw&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; CustomError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Route&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;  catch&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; instanceof&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; CustomError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;      throw&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; err&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; instanceof&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;      throw&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; CustomError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; CustomError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;unknownError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; CustomError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;  errorCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;    super&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;    Object&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#59873A;--shiki-dark:#80A665&quot;&gt;setPrototypeOf&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt; CustomError&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#998418;--shiki-dark:#B8A965&quot;&gt;prototype&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A65E2B;--shiki-dark:#C99076&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;errorCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt; msg&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nice，让我们来看看使用效果吧。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/7LaBo5QqRWFAHVm.gif&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;ScreenShot 2023-08-09 12.38.53.gif&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;最后&quot;&gt;最后&lt;/h2&gt;
&lt;p&gt;文章写到这里就算是结束了，取决你自己的需求，你还可以做如下事情：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将它和 useSWR 组合起来，这样你就不必为每个网络接口单独封装函数了&lt;/li&gt;
&lt;li&gt;在 fetch 中封装更多逻辑，比如自动携带登录所需的 token 和在登录状态过期时自动的路由拦截&lt;/li&gt;
&lt;li&gt;单独处理非 json 交互的接口，比如文件上传下载&lt;/li&gt;
&lt;li&gt;说服你的后端，让它用 ts 来给你写接口，这样这些路由定义就不需要你手动写了，也更安全&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你感谢兴趣，以下是一些被用到但是没有在文章中展开的类型计算。
当然，这份实现未必是最好的，如果你有任何改进的想法，欢迎与我讨论，非常感谢。&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes vitesse-light vitesse-dark&quot; style=&quot;background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; ValueOfObjectArray&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  RestKey&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; unknown&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[]&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; UnionToTuple&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;keyof&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RestKey&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RestKey&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; First&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Rest&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    ?&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; First&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; keyof&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;First&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;],&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;ValueOfObjectArray&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Rest&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;      :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0ADA0;--shiki-dark:#758575DD&quot;&gt;// https://github.com/type-challenges/type-challenges/issues/2835&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; LastUnion&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; UnionToIntersection&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; L&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; L&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; UnionToTuple&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Last&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; LastUnion&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;never&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [...&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;UnionToTuple&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Exclude&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Last&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Last&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; AddPrefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; P&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt; &apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RouteItem&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      path&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;P&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}${&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      data&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;      errCode&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#B56959;--shiki-dark:#C98A7D&quot;&gt;errCode&lt;/span&gt;&lt;span style=&quot;color:#B5695977;--shiki-dark:#C98A7D77&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; AddPrefixForArray&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;Arr&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Arr&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Arr&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; A&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; B&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;AddPrefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Prefix&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;AddPrefixForArray&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;B&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;    :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; DistributedHelper&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; RouteItem&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; Helper&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; ArrayToUnion&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[]&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E754F;--shiki-dark:#4D9375&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; UnionToIntersection&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;U&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;  U&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt;U&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B07D48;--shiki-dark:#BD976A&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;infer&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#999999;--shiki-dark:#666666&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  ?&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; I&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#AB5959;--shiki-dark:#CB7676&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:#2E8F82;--shiki-dark:#5DA994&quot;&gt; never&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>远程兼职一年后</title><link>https://hyoban.cc/work-one-year</link><guid>work-one-year</guid><description>不知不觉，我已经工作一年了。回想起来，有些东西是值得被记录下来的。</description><pubDate>Sun, 09 Jul 2023 16:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;故事的开始&quot;&gt;故事的开始&lt;/h2&gt;
&lt;p&gt;去年过年的时候，我接到了一个外包，基本上就是爬数据，写后端，写网页。
当时我还只会写一点安卓，不会写前端，后端的话也只会写一点 Python 或者 Java。
好在时间并不紧张，我照着 &lt;a href=&quot;https://fullstackopen.com/zh&quot;&gt;深入浅出现代 Web 编程&lt;/a&gt; 边学边写，完成了任务。
是的，我一开始用的是 React，用 &lt;a href=&quot;https://mui.com&quot;&gt;mui&lt;/a&gt; 作为组件库。&lt;/p&gt;
&lt;h2 id=&quot;开始入门&quot;&gt;开始入门&lt;/h2&gt;
&lt;p&gt;在去年三月份的时候，我偶然间看到 &lt;a href=&quot;https://antfu.me&quot;&gt;Anthony Fu&lt;/a&gt; 在 b 站的 &lt;a href=&quot;https://www.bilibili.com/video/BV1ia411b7jY&quot;&gt;第一次直播&lt;/a&gt;。
直播里，我第一次了解到原子化 CSS 以及 Vue 和 Vite 生态中的各种插件。
直播后，我沿着他的 &lt;a href=&quot;https://github.com/antfu/vitesse&quot;&gt;Vue 起手模板&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/antfu/antfu.me&quot;&gt;博客&lt;/a&gt; 中涉及到的技术去学习前端。
也没个特定顺序，就只是学些自己感兴趣的东西。&lt;/p&gt;
&lt;h2 id=&quot;接触开源&quot;&gt;接触开源&lt;/h2&gt;
&lt;p&gt;到了六月份，我接触到 &lt;a href=&quot;https://github.com/usememos/memos&quot;&gt;memos&lt;/a&gt; 这个开源项目，并修复了我处理的 &lt;a href=&quot;https://github.com/usememos/memos/issues/90&quot;&gt;第一个 issue&lt;/a&gt;。
后面我日常修复一些 bug，实现一些小功能。
在十月份我联系了作者，&lt;a href=&quot;https://github.com/boojack&quot;&gt;Steven&lt;/a&gt; 人特别好，很欢迎大家去参与开源。
他拉我进了组织，从他的代码中我学习到了很多。
可惜的是，随着今年兼职和学校方面的事情变多，提交 PR 的次数也越来越少了。&lt;/p&gt;
&lt;h2 id=&quot;找工作&quot;&gt;找工作&lt;/h2&gt;
&lt;p&gt;受疫情影响，上半年的时候我一直在家里，就只是不断学习和做一些别的事情。
等快到暑假，大概是在家待得太久了，家里人开始催我出去找个工作。
最开始的时候是让一个认识的人推荐一个小公司的职位，不过工资太低，去干活啥基本啥都存不下来，而且后来也没招我。&lt;/p&gt;
&lt;p&gt;去年 7 月 10 号中午，偶然在 V2EX 上看到 &lt;a href=&quot;https://twitter.com/kxycigaret&quot;&gt;祥岩&lt;/a&gt; 发的一个 &lt;a href=&quot;https://www.v2ex.com/t/865194&quot;&gt;招聘帖&lt;/a&gt;。
从技术栈和工作条件来看，这份工作很适合我，于是我直接加了微信。
简历是放在我的个人网站上的，我就直接把链接发给他。&lt;/p&gt;
&lt;p&gt;神奇的是，我们只是在微信上简单交流了一下，晚上他就发了 offer 给我，拉我进了 GitHub 组织和飞书。
是的，没有技术面试，没有合同，我一度以为是骗子。
因为我还看到他另外一篇很不靠谱的帖子，「&lt;a href=&quot;https://www.v2ex.com/t/864079&quot;&gt;大家好，我开始了一项公开创业计划，目标是九个月获得五千万元投资&lt;/a&gt;」。
（不出意外，这个计划失败了）&lt;/p&gt;
&lt;p&gt;不过很快，这种疑虑就被我打消了，因为我开始了我的工作，写数据可视化工具的网页。
同时，我做好了准备，只要一个月内没有发工资，我就跑路，这样也不会亏什么。&lt;/p&gt;
&lt;h2 id=&quot;一些工作中的体验&quot;&gt;一些工作中的体验&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;最初的时候，我很担心自己的能力不足以支持我完成任务，后来就不会了。&lt;/li&gt;
&lt;li&gt;我是完全远程兼职的，大约是每周工作五天，每天工作四个小时。
不过我可以在任何时间工作，也不会有时间考核，很多时候完成任务所需要的时间都不足这个时长。&lt;/li&gt;
&lt;li&gt;工作内容完全不限于前端，可能去写爬虫了，可能去搞定自动部署了。
大多数都是我乐于完成的任务，不会有需要在技术选择上妥协吃屎的地方。&lt;/li&gt;
&lt;li&gt;在学校有一笔稳定的收入真的很好，我每个月会有五千块钱。
换了一台笔记本，给我妈买了一台手机，到现在也还存了接近五万块。&lt;/li&gt;
&lt;li&gt;通过不断学习，不断将新的技术应用到工作中，我感觉自己的能力在不断提升。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;认识了很有意思的李书&quot;&gt;认识了很有意思的李书&lt;/h2&gt;
&lt;p&gt;在刚进入团队的时候，除了祥岩，就只有我和 &lt;a href=&quot;https://github.com/lsby&quot;&gt;李书&lt;/a&gt; 了。
李书是负责写后端的，他很怪，既很深的理解函数式编程和类型系统，又会在代码里坚持使用中文变量 和 var。
他还懂得数学和心理学哲学方面的知识，自己的图书馆里有很多我不想看，也看不明白的书。
在他的推荐下，我看完了阿德勒的两部曲，受益匪浅。&lt;/p&gt;
&lt;p&gt;我们之间的一些共通特点将我们一起聚集到这个团队，所以我们很自然的成为了好朋友。
即使我们不处在一个城市，这也不妨碍我们时常聊天几个小时。
下面是李书当时离职发的小作文，我觉得应该很多人会觉得他很酷很好。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s2.loli.net/2023/08/07/jf7ZLmvXduq342F.jpg&quot;&gt;img_v2_fcc096eb-f378-49ad-9b4d-9957819772eg.jpg&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;从-vuejs-到-react&quot;&gt;从 Vue.js 到 React&lt;/h2&gt;
&lt;p&gt;如你所见，在招聘的那个帖子里，关于前端框架的选择是 Vue.js。
但是在几个项目之后，就换到了 React，主要有如下几点考虑。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://volarjs.github.io&quot;&gt;volar&lt;/a&gt; 的体验不佳，经常需要 reload；相对比写 React 的 tsx 不需要安装额外的插件，也更加稳定。
&lt;ul&gt;
&lt;li&gt;volar 具体的体验可以看 &lt;a href=&quot;https://twitter.com/isukkaw/status/1688077064359079936&quot;&gt;这条推&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;React 的生态更加好，比如 &lt;a href=&quot;https://www.radix-ui.com&quot;&gt;radix-ui&lt;/a&gt; 和 &lt;a href=&quot;https://www.framer.com/motion&quot;&gt;framer-motion&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基于同样的 DX 考虑，也把原本祥岩配置的一套 eslint 给换了，因为它严重影响保存代码时的性能。&lt;/p&gt;
&lt;p&gt;所以，你可以感受到，大家的关系与其说是雇佣关系，不如说是伙伴，我们每个人都有提出自己的想法和决策的权利。&lt;/p&gt;
&lt;h2 id=&quot;小插曲认识-zeabur&quot;&gt;小插曲，认识 zeabur&lt;/h2&gt;
&lt;p&gt;远程兼职的生活，让我对于去公司 996 赚钱的想法越来越淡，我希望可以在毕业后继续这样的生活，但是现在我还没有解决经济问题。
今年上半年一个偶然的机会，看到了 &lt;a href=&quot;https://zeabur.com/zh-CN&quot;&gt;zeabur&lt;/a&gt; 在招前端实习。
我想，如果我能再找到这一份工作，就不需要再担心了。&lt;/p&gt;
&lt;p&gt;就和这份工作一样，联系上微信发了简历之后，我和 zeabur 的两位创始人&lt;a href=&quot;https://twitter.com/yuaanlin&quot;&gt;沅霖&lt;/a&gt; 和 &lt;a href=&quot;https://twitter.com/CoooolXyh&quot;&gt;宇航&lt;/a&gt; 在北京的一家咖啡馆见了面。
聊了一会，并一起在咖啡馆写了会代码，就加入了。&lt;/p&gt;
&lt;p&gt;zeabur 也是一个很酷的公司，团队里的人都很厉害。
不过很可惜，加入后一周多，我因为需要投入更多时间推进自己的小论文，时间上实在是不够，就退出了。&lt;/p&gt;
&lt;h2 id=&quot;现在的情况&quot;&gt;现在的情况&lt;/h2&gt;
&lt;p&gt;和去年只有我，祥岩，李书三个人不同，现在我们 &lt;a href=&quot;https://github.com/planet-matrix&quot;&gt;团队&lt;/a&gt; 已经有了七个人，业务也在不断扩展。
如果你对我们的工作感兴趣，可以查阅我们的 &lt;a href=&quot;https://datadata.feishu.cn/wiki/ZSrDw1ioji9vPIkfSrNcJBREnWd&quot;&gt;Grow in Public&lt;/a&gt; 文档来了解更多。&lt;/p&gt;
&lt;p&gt;研二的这个暑假，没有学校的事情需要处理，但是总共只有三周时间，第一周在家里，后面两周去了湖北。
我算是体验了一点数字游民的感觉，带上电脑，可以去任何地方。
睡觉睡到自然醒，随便出去找点吃的，无聊的时候刷刷 Twitter，看看视频，在任何时间写一点代码就好。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;../../assets/images/posts/VIr9hGaWwMD1BYm.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;IMG_20230805_134548.jpg&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;</content:encoded></item></channel></rss>